www.horizon3.ai
Open in
urlscan Pro
104.197.16.226
Public Scan
Submitted URL: https://mdr.esentire.com/e/651833/-and-indicators-of-compromise-/2prwxj/954496554?h=XS-yr_q5Bxn_GjrttiKgRB9NZSvjgjUGlKhQA...
Effective URL: https://www.horizon3.ai/moveit-transfer-cve-2023-34362-deep-dive-and-indicators-of-compromise/
Submission: On June 14 via api from US — Scanned from DE
Effective URL: https://www.horizon3.ai/moveit-transfer-cve-2023-34362-deep-dive-and-indicators-of-compromise/
Submission: On June 14 via api from US — Scanned from DE
Form analysis
0 forms found in the DOMText Content
* NodeZero™ * What is NodeZero? * Internal Pentesting * External Pentesting * Documentation * FAQ * Credential Attacks * Ransomware Impact * Learn * Year in Review 2022 * By Industry * Healthcare * Public Sector * Attack Content * Log4Shell * Security Controls * Compliance In Security * Effective Security * For Splunk Logging * Purple Team Culture * Vulnerable ≠ Exploitable * Customer Stories * Blogs * Customer Success * Hack the Box * Red Team * Disclosures * Videos * Podcasts * Whitepapers * About * Our Vision * The Team * Join Our Team * Contact Us * In the News * Awards * Events * Partners * Partners * MSSPs and MSPs * Partner Portal * Log In * Try NodeZero * Demo * Free Trial Select Page * NodeZero™ * What is NodeZero? * Internal Pentesting * External Pentesting * Documentation * FAQ * Credential Attacks * Ransomware Impact * Learn * Year in Review 2022 * By Industry * Healthcare * Public Sector * Attack Content * Log4Shell * Security Controls * Compliance In Security * Effective Security * For Splunk Logging * Purple Team Culture * Vulnerable ≠ Exploitable * Customer Stories * Blogs * Customer Success * Hack the Box * Red Team * Disclosures * Videos * Podcasts * Whitepapers * About * Our Vision * The Team * Join Our Team * Contact Us * In the News * Awards * Events * Partners * Partners * MSSPs and MSPs * Partner Portal * Log In * Try NodeZero * Demo * Free Trial MOVEIT TRANSFER CVE-2023-34362 DEEP DIVE AND INDICATORS OF COMPROMISE by Zach Hanley | Jun 9, 2023 | Blog, Red Team On May 31, 2023, Progress released a security advisory for their MOVEit Transfer application which detailed a SQL injection leading to remote code execution and urged customers to update to the latest version. The vulnerability, CVE-2023-34362, at the time of release was believed to have been exploited in-the-wild as a 0-day dating back at least 30 days. Soon after publication, a flurry of threat intelligence by various companies was released which indicated that this vulnerability was exploited further back than initially thought – GreyNoise seeing activity 90 days prior and Kroll reporting similar activity as far back as 2021. The attacks have been attributed to the cl0p ransomware gang, which is attributed to several other recent 0-day ransomware campaigns such as PaperCut, GoAnywhere MFT, SolarWinds Serv-U, and Accellion FTA. Figure 1. cl0p 0-day activities TAKING A PEEK – PATCH DIFF’ING Taking a look at the differences between the vulnerable and patched versions we find three interesting areas. The first difference found in the function UserGetUsersWithEmailAddress() appears to update a SQL query from a concatenated string of several arguments passed in, to a safer looking SQL builder utility. This helper function is reachable from many code paths, interestingly from several unauthenticated paths via guestaccess.aspx. Figure 2. UserGetUserWithEmailAddress() function differences The second difference found in the function SetAllSessionVarsFromHeaders() removes the entire function and removes the only caller of that function from the machine2.aspx handler, SILMachine2, when the received Transaction is session_setvars. Unfortunately machine2.aspx requests will only be processed if coming from localhost. Figure 3. SetAllSessionVarsFromHeaders() function removed The last difference found in GetFileUploadInfo() adds a single statement which changes the way the uploadState is set by first checking if the State is null before using a new decryption helper DecryptBytesForDatabase. Figure 4. GetFileUploadInfo() function differences A PATH TO EXPLOITATION Foreword: looking at public threat intelligence about the series of endpoints being hit and the types of indicators of compromise, we aren’t entirely sure the path we’ve found is the exact same abuse of the patched functionality mixed with abuse of intended functionality. There are likely several paths to exploitation – there are many like it, but this one is ours. Given that the description of the vulnerability was a SQL injection, the path to the apparent patch in UserGetUsersWithEmailAddress() was pursued first. While paths were discovered to reach this function from an unauthenticated point-of-view, we were unable to discover a way to have the controllable arguments passed to it without being ‘cleaned’ by XHTMLClean(), which converts the typical unsafe SQL characters to their HTML encoded counterparts. THE PATH TO UNCLEAN INPUT We shifted our focus to the other removed function SetAllSessionVarsFromHeaders(). We found that this function had the restriction that only localhost is allowed to route. Threat actors were observed hitting the /moveitisapi/moveitisapi.dll?action=m2 so we were hopeful that we could find a path from moveitisapi.dll to SetAllSessionVarsFromHeaders(). moveitisapi.dll is a compiled C program of which we can analyze with Ghidra. Opening it up, we find that the function at 0x180080920, dubbed action_m2, is responsible for parsing requests that contain the action=m2 request parameter. The action_m2 function takes requests, and forwards those requests on to the machine2.aspx endpoint only if the passed in header X-siLock-Transaction is equal to folder_add_by_path. Figure 5. action_m2() function in MOVEitISAPI.dll Unfortunately, thats not ~exactly~ how it works. The function that extracts the X-siLock-Transaction header to compare its value to folder_add_by_path has a bug. It will incorrectly extract headers that end in X-siLock-Transaction, so an attacker can trick the function to passing the request onto the machine2.aspx by providing a header such as xX-siLock-Transaction=folder_add_by_path and additionally providing the correctly formatted header with our own arbitrary transaction to be executed by the machine2.aspx endpoint. Figure 6. Transaction bypass via crafted headers With entry into machine2.aspx via this backend relay of our request, we can now reach SetAllSessionVarsFromHeaders() when we pass in a transaction of session_setvars. Our Cookie header as well as all other X-siLock- headers will be passed in with our request. Analyzing the functionality of this removed function further, it will parse all headers, and if the header starts with X-siLock-SessVar it will set the corresponding variable of the session in use to the arbitrary value provided. For example, X-siLock-SessVar0: MyUsername: sysadmin will set the username of session to the builtin sysadmin. This capability unfortunately does not enable you to just assume the sysadmin role and use the application, but it does provide access to set many variables loaded in code paths which bypass being cleaned by the XHTMLClean() function from earlier. THE PATH TO SQL INJECTION The path to the vulnerable UserGetUsersWithEmailAddress() function we took was via an unauthenticated call to guestaccess.aspx when the passed Transaction is secmsgpost. The full call chain of relevant calls is: guestaccess.aspx -> SILGuestAccess -> SILGuestAccess.PerformAction() -> MsgEngine.MsgPostForGuest() -> UserEngine.UserGetSelfProvisionUserRecipsWithEmailAddress() -> UserEngine.UserGetUsersWithEmailAddress() While we will not analyze the call chain in depth and all of the variable setting whack-a-mole that was needed to reach the vulnerable function, the crux of what changed with our access to session variable manipulation is in the very beginning of guestaccess.aspx’s handler in SILGuestAccess. The main function calls this.m_pkginfo.LoadFromSession(), which sets variables from session variables that we can now influence with session_setvars. Figure 7. LoadFromSession() loads variables from the session Along the call chain, the SelfProvisionedRecips value is extracted as a list of comma separated email addresses and never cleaned before being passed to our vulnerable function. Inspecting how the SQL query is built in our vulnerable function, we see the InstID, EscapeLikeForSQL(EmailAddress), and finally EmailAddress are formatted into the query statement. The final query statement looks like: SELECT Username, Permissions, LoginName, Email FROM users WHERE InstID=9389 AND Deleted=0 AND (Email='<EmailAddress>' OR Email LIKE (%EscapeLikeForSQL(<EmailAddress>)) or Email LIKE (EscapeLikeForSQL(<EmailAddress>)); The part of the query AND Email='<EmailAddress>' has our uncleaned argument of SelfProvisionedRecips inserted into the query. The only caveat to this injection, is that just prior to the call the SelfProvisionedRecips variable is split on comma’s (,). Our injected SQL statement should avoid having commas to continue proper execution. We can work around needing commas by reusing the SQL injection several times to do sequential statements such as INSERT then UPDATE. All of this information combined, an example request in Python that will set the right session variables via a request to the action=m2 endpoint and then a request to the guestaccess.aspx endpoint to inject would look like the following: Figure 8. Python script excerpt to perform SQL injection THE PATH TO ADMINISTRATOR SESSION With the ability to read and write any data within the MOVEit database, our next goal is to achieve elevated permissions from an unauthenticated session. Threat intelligence showed logs that the attackers would hit the /api/v1/auth/token endpoint, which is handled by MOVEit.DMZ.WebAPI. Authentication is handled here, and based on the session_grant parameter passed in, different authentication paths are taken. Several of these paths were explored, some more than others, but the path we decided to go after is when session_grant=external_token, which is handled by the function GrantTokenFromExtenralToken(). This type of authentication flow is used when the MOVEit Transfer application has been configured to use federated logins, specifically from Microsoft Outlook acting as the identity provider. Assuming the application has been configured to use a federated login flow, users send a payload to the /api/v1/auth/token endpoint with a payload that contains a RS256 JWT. The decoded JWT should look like the following: Figure 9. Example RS256 JWT The important information here is that the MOVEit Transfer application will reach out the URL in the amurl field to retrieve the certificate that matches the given x5t signature to extract and validate that the JWT was in fact signed by the identity provider. Because we control the content of the JWT, we can point it to our own endpoint that hosts our own matching certificate that will pass validation. We ultimately use the SQL injection from the previous paths to configure the database to think the application is configured this way, to trust our identity provider URL, and inject an external token for the builtin sysadmin user. We also use the SQL injection to pass several checks along the way to allow the sysadmin user to be able to login from any IP address. Combining it all together we now obtain an access token for the sysadmin user and use it to list files they have access to. Figure 10. Chaining issues to obtain sysadmin access token THE PATH TO REMOTE CODE EXECUTION The last step of this exploit chain is to abuse the sysadmin access token to achieve remote code execution. Threat actors were observed hitting the /api/v1/folders, /api/v1/folders/<folder_id>/files?uploadType=resumable, and /api/v1/folders/<folder_id>/files?uploadType=resumable&fileId=<file_id> endpoints. Pairing that knowledge with the last difference observed in the patch related to file uploads, we begin looking at the file upload handlers in within MOVEit.DMZ.WebApi. The only path to the function that was patched, GetFileUploadInfo(), is when a file upload is resumed that was previous in progress – which matches the call to /api/v1/folders/<folder_id>/files?uploadType=resumable&fileId=<file_id>. The specific variable they now attempt to protect is this._uploadState. Examining where that variable is referenced in the .NET DLL, we see that the function DeserializeFileUploadStream() uses it to create a MemoryStream object and then immediately uses it in a call to BinaryFormatter().Deserialize(). This is a classic .NET deserialization vulnerability. Normally, the uploadState variable would not be under attacker influence, but because we have a SQL injection, we can influence the field from which that variable is set. Figure 11. BinaryFormatter.Deserialize() on input we control Looking at the state of the database from which the uploadState variable is set, we find that the State value is NULL. We need this State value to contain our base64 encoded serialized .NET payload. Figure 12. Database tabe fileuploadinfo schema Using a tool like ysoserial.net, we generate a payload for the formatter in use. ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -c "cmd.exe /C echo DIRTY MIKE AND THE BOYS WERE HERE > C:\Windows\Temp\message.txt" -o base64 Figure 13. ysoserial payload generation The only hurdle to overcome is, that when reading the State field from the database, it expects the data to be encrypted with an organization specific encryption key. We spent some time looking at how we could extract and re-implement the encryption, but thankfully theres a simple workaround. When initiating the file upload, you can optionally provide a Comment. This comment is encrypted with that organization specific key. We can provide our base64 ysoserial payload as the comment when initiating the upload and have it do the heavy lifting for us. To prepare the application to reach this bit of code requires several interactions: 1. Retrieve the user’s FolderID by requesting /api/v1/folders 2. Retrieve a FileID by starting a file upload by requesting /api/v1/folders/<folder_id>/files?uploadType=resumable and providing our payload as the Comment 3. Use SQL injection to copy the Comment to the State field 4. Resume the file upload triggering loading of State into uploadState and calling BinaryFormatter.Deserialize(uploadState) The full exploit chain in action to write a file to C:\Windows\Temp\message.txt. Figure 14. Executing the proof-of-concept exploit Figure 15. Remote Code Execution Our proof of concept can be found on our GitHub. POST-EXPLOITATION BONUS If you find yourself on a MOVEit Transfer server that was deployed via the Azure Marketplace (and in some other cases), in C:\MOVEitDMZ_Install.INI you will find cleartext credentials for the provisioned sysadmin account, database credentials, and the service credential. All great targets for lateral movement. Figure 16. MOVEitDMZ_Install.INI This file is used for unattended installs, and users are given the optional to preserve it after normal installations as well. > MOVEitDMZ_Install.INI – The parameter input file for the installation. You can > create an INI file by performing a standard MOVEit DMZ installation and NOT > deleting the file at the end. Once you have the INI file, you can modify it in > a text editor to customize the input for use as an unattended install. INDICATORS OF COMPROMISE Our exploit path may not be similar to paths taken by recent threat actors, but there are several places to look for indicators. The database tables userexternaltokens, trustedexternaltokenproviders, and hostpermits all had entries inserted to achieve the sysadmin access token. The fileuploadinfo table was altered to obtain RCE. One should inspect these tables to look for any anomalous entries. Log entries for endpoint traffic can be found in the following areas: * <InstallDir>/Logs/DMZ_WebApi.log when requests are made to /api/v1/ endpoints * <InstallDir>/Logs/DMZ_WEB.log when requests are made to /guestaccess.aspx and relayed messages to /machine2.aspx * <InstallDir>/Logs/DMZ_ISAPI.log when requests are made to /moveitisapi/moveitisapi.dll?action=m2 HOW CAN NODEZERO HELP YOU? Let our experts walk you through a demonstration of NodeZero, so you can see how to put it to work for your company. Schedule a Demo info@horizon3.ai • 650-445-4457 Contact Us FOLLOW US RECENT POSTS * MOVEit Transfer CVE-2023-34362 Deep Dive and Indicators of Compromise 5 days ago * Clients Want Assessments to Prove Service Efficacy 1 week ago SUBSCRIBE TO COMMUNITY UPDATES © 2022 All Rights Reserved. | Privacy Policy | Support Policy | Terms and Subscriptions We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept All”, you consent to the use of ALL the cookies. However, you may visit "Cookie Settings" to provide a controlled consent. Cookie SettingsAccept All Manage consent Close PRIVACY OVERVIEW This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the ... Necessary Necessary Always Enabled Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously. CookieDurationDescription__cfruidsessionCloudflare sets this cookie to identify trusted web traffic._GRECAPTCHA5 months 27 daysThis cookie is set by the Google recaptcha service to identify bots to protect the website against malicious spam attacks.cookielawinfo-checkbox-advertisement1 yearSet by the GDPR Cookie Consent plugin, this cookie is used to record the user consent for the cookies in the "Advertisement" category .cookielawinfo-checkbox-analytics11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".cookielawinfo-checkbox-functional11 monthsThe cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".cookielawinfo-checkbox-necessary11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".cookielawinfo-checkbox-others11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.cookielawinfo-checkbox-performance11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".CookieLawInfoConsent1 yearRecords the default button state of the corresponding category & the status of CCPA. It works only in coordination with the primary cookie.OptanonConsent1 yearOneTrust sets this cookie to store details about the site's cookie category and check whether visitors have given or withdrawn consent from the use of each category.viewed_cookie_policy11 monthsThe cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data. Functional Functional Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features. CookieDurationDescriptionAnalyticsSyncHistory1 monthLinkedIn - Used to store information about the time a sync took place with the lms_analytics cookiebcookie2 yearsLinkedIn sets this cookie from LinkedIn share buttons and ad tags to recognize browser ID.bscookie2 yearsLinkedIn sets this cookie to store performed actions on the website.langsessionLinkedIn sets this cookie to remember a user's language setting.li_gc2 yearsLInkedIn Used to store consent of guests regarding the use of cookies for non-essential purposeslidc1 dayLinkedIn sets the lidc cookie to facilitate data center selection.UserMatchHistory1 monthLinkedIn sets this cookie for LinkedIn Ads ID syncing. Performance Performance Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors. CookieDurationDescription_calendly_session21 daysCalendly, a Meeting Schedulers, sets this cookie to allow the meeting scheduler to function within the website and to add events into the visitor’s calendar. Analytics Analytics Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc. CookieDurationDescription_ga2 yearsThe _ga cookie, installed by Google Analytics, calculates visitor, session and campaign data and also keeps track of site usage for the site's analytics report. The cookie stores information anonymously and assigns a randomly generated number to recognize unique visitors._ga_V462VSRXXS2 yearsThis cookie is installed by Google Analytics.6suuid2 years6sense is a B2B predictive intelligence engine for marketing and sales.CONSENT2 yearsYouTube sets this cookie via embedded youtube-videos and registers anonymous statistical data.pardotpastThe pardot cookie is set while the visitor is logged in as a Pardot user. The cookie indicates an active session and is not used for tracking.visitorId1 yearSalesforce Advertisement Advertisement Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads. CookieDurationDescriptionVISITOR_INFO1_LIVE5 months 27 daysA cookie set by YouTube to measure bandwidth that determines whether the user gets the new or old player interface.YSCsessionYSC cookie is set by Youtube and is used to track the views of embedded videos on Youtube pages.yt.innertube::nextIdneverThis cookie, set by YouTube, registers a unique ID to store data on what videos from YouTube the user has seen.yt.innertube::requestsneverThis cookie, set by YouTube, registers a unique ID to store data on what videos from YouTube the user has seen. Others Others Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet. CookieDurationDescriptionlpv97107330 minutesNo description SAVE & ACCEPT Powered by