Hopefully you enjoyed Adam Fernandez’s recent Cool Tools post, Nmap For Penetration Tests. In this next post in the series, I’d like to introduce another great penetration testing tool: Nuclei.
Nuclei is an outstanding enumeration tool that performs a myriad of checks against the systems and services it discovers. It does particularly well against web applications. It also tests for known vulnerabilities, can pull versioning information from systems, and can try default credentials.
Installing Nuclei
You can use apt to easily install it on Kali Linux:
apt install nuclei
Templates
Nuclei uses saved templates to perform tests against systems. This lets users create new templates as new discoveries are made. You can also generate custom templates for tasks you often use or things you discover yourself.
There are a lot of different options when using the tool, but generally, when I’m using it for external network and web application tests, I just pass a target or a list of targets to the program.
To get a full list of options, all you need to do is this command:
nuclei –help
To scan a single device, simply use:
nuclei -u {Target}
Here is an example from a recent penetration test. There was only one target, so I simply passed that target to Nuclei:
You can see that Nuclei examined the responses from the web server and found that security-related headers were missing.
Here is an example using Nuclei against a list of targets. I put the targets for the engagement into a file called targets & ran the following command:
nuclei -list {File}
Real-World Nuclei Discoveries
Here are some examples of useful things Nuclei has found for me during various penetration tests.
Here Nuclei found a Solr panel, revealed the version running, and shared it in a human-readable format easy for me to use as a pentester:
Here Nuclei did some testing against a host on an internal network and successfully performed a Log4j exploit. It then reported the success with a critical flag as this could lead to remote code execution (RCE):
I’ve also found Nuclei’s ability to try default passwords to come in handy when testing.
As an example, I use a self-hosted Git instance and changed the root account’s password to one that Nuclei uses. You can see how Nuclei successfully logged into the GitLab and notified me:
Thanks for Reading
There’s a lot more to Nuclei, but this is a good start. Thanks for taking a look at this post, and I hope you’ll be back for future Cool Tools posts in the series.
In Scottie’s recent Cool Tools blog about discovery tools, he, like many network penetration testers, starts with Nmap. Nmap is a great discovery tool with many flags and scripts. In this post, I will expand upon Scottie’s ideas and delve more deeply into what Nmap has to offer, specifically for network penetration testing.
Hosts Marked as Down When They Are Up
If you find you have problems with Nmap flagging hosts as down, even though they should be up, I would recommend adding these flags to your command:
-PS – SYN ping scan. You can optionally add a port number after this.
–PA – ACK ping scan. You can optionally add a port number after this.
-PE – Typical ICMP ping echo request, typically blocked by firewalls.
-PP – ICMP timestamp ping query, which can sometimes get around misconfigured firewalls.
-PM – ICMP subnet mask ping query, also good for evading misconfigured firewalls.
-PO -This issues an IP protocol ping, which seems to work a lot of times when other probes don’t.
Putting that all together, my typical scans are now starting to look like this:
-oA [output] – Output in all file formats simultaneously.
-sSUV – Combo of -sS (TCP SYN scan), -sU (UDP scan), and -sV (service version scanning).
–unique – Scan each IP only once (e.g. if raxis.com resolves to 1.1.1.1, and both are targets, only scan 1.1.1.1 once).
–resolve-all – If an DNS name resolves to multiple IPs, scan each IP.
-T4 – Go a bit faster than default, recommended for modern networks (unless you’re trying to be quiet).
-O – Try to determine operating system.
-p T:-,U:53,161 – Scan all TCP ports as well as UDP ports 53 (DNS) and 161 (SNMP).
-vv – Be very verbose in output.
-iL [targets] – Use the targets in the specified file.
Additionally, when using -oA, if your scan gets interrupted, you can use this to resume the scan where it left off:
nmap --resume
The Nmap Scripting Engine
The Nmap Scripting Engine (NSE) provides 604 scripts and 139 libraries at the time I’m writing this. That number grows each year, so, needless to say, this is a very useful tool for penetration testers or for blue teams checking out possible vulnerabilities in their systems. I’ll discuss some Useful NSE scripts here, and I encourage you to take a look to see if there are others that would be helpful for your needs.
Shodan
Queries the Shodan API for the targets. Follow the instructions on the NSE site to setup a Shodan API key.
Before starting any penetration test, you must know the scope of the work. Depending on the type of assessment, this could be specific hosts or full ranges of IP addresses with live hosts scattered throughout. If the scope is the latter, then it is a good idea to initially identify which hosts are live and then discover common, known vulnerabilities on the hosts. This will narrow down the attack surface and potential attack vectors to help establish a list of priority targets during the assessment.
There are several tools that I like to use when I start a new assessment. Some are free open-source tools, while others are commercial.
Open-Source Tools
Nmap
The first tool I always use in Nmap. Nmap is an open-source tool that can identify live hosts and services by conducting protocol enumeration across systems. It can also be used for specific configuration checks or even as a vulnerability scanner by using the Nmap Scripting Engine (NSE).
Even when I use Nmap with vulnerability scanners, I still regularly utilize Nmap scripts. Nmap come pre-installed with Kali Linux but can easily be installed on any Linux or Windows system. Using Nmap for the first time can be intimidating, but after using it for a bit, users often find it very easy and reliable. I usually run the initial scans by ignoring ICMP checks (Ping) and just assume that all hosts are live. I do this because some network admins like to disable ICMP on live hosts as a security measure (Good on ’em!).
nmap -v -A --open -Pn -iL targets.txt -oA nmap_scan
Note: -Pn disables the ICMP check.
If the scope is extremely large and the Nmap scans won’t complete in the time allowed, I enable the ICMP check by remove the “-Pn”:
nmap -v -A --open -iL targets.txt -oA nmap_scan
Below is a screen shot of a typical initial scan I perform on network assessments (internal & external):
The commands above are easy to learn once you use them a few times, and I’ll cover what is going on here.
First, I like to use the “-v” which enables verbose outputs.
The “-A” enables OS and version detection, as well as script scanning and traceroute output.
“-Pn” disables ICMP ping for host discovery, causing the scan to assume that all hosts are live.
Next, we have the IP range, in this instance a standard /24 internal network. If I have specific hosts or multiple ranges to target, I will create a text file and use the “-iL” switch to point to the text file.
Lastly, I like to output the results to all supported formats by setting “-oA.” The reason I do this is because I like to ensure I have the different file types for future use. For example, the .nmap output is easy to read when I want to scan through the output. I typically use the XML output for importing into Metasploit or when using Eyewitness to enumerate web hosts.
There are quite a few good cheat sheets out there too if you let the Googles do the work for you. If not, follow this series for more Nmap tips and tricks in the future.
Masscan
Another tool similar to Nmap is Masscan. Masscan is a TCP port scanner that scans faster than Nmap. Unlike Nmap, Masscan requires you to define the ports you want to scan. I typically don’t use Masscan and just stick with Nmap, but it’s a good option if you want to quickly look for specific open ports on a network.
OpenVas
Another tool I use on occasion is OpenVAS (a.k.a. Greenbone), a vulnerability scanner. Greenbone offers a commercial version, but there is an open-source version available as well. I’ve used this many times when I am unable to access my usual vulnerability scanners. One downside to the tool is that It can be difficult to install. I recently followed the instructions for installing with Kali and ran into quite a few issues with the install. I’ve also noticed that the initial setup can take some time due to the signatures that are downloaded. If purchasing a commercial vulnerability scanner is too expensive, then Greenbone is definitely worth looking into, despite its challenges.
Commercial Tools
Nessus
By far, my favorite vulnerability scanner is Tenable’s Nessus. There is a free version available, but it’s limited to 16 IP addresses. If you are doing a lot of vulnerability scanning or time-boxed penetration tests, then it might be worth looking into purchasing this.
The thing I like most about Nessus is how I can sort by vulnerabilities across all hosts in a given scan. I can also sort these vulnerabilities by risk rating, which helps me narrow down critical or specific vulnerabilities to exploit or signs that a host or service may be a high priority for a closer look.
When viewing Nessus results, never ignore the informational findings. They often provide clues that more may be going on on a host or service than you realize at first glance.
Nexpose
Another great vulnerability assessment tool is Nexpose, owned by Rapid7. Nexpose is similar to Nessus, as it provides similar vulnerabilities. There are some slight differences in the way the products display results.
Nexpose is built around “sites.” Each site has defined hosts or IP ranges under it. From there each host’s vulnerabilities will be listed. The easiest way I’ve found to list out all vulnerabilities is to create a report from the site I’m working in.
Besides greater extensibility, one major advantage with Nexpose is that it ties in with Rapid7’s vulnerability management product, InsightVM. If you’re looking for a full vulnerability management solution and not just a vulnerability scanner, Nexpose is a good option to check out.
There are many other tools that I use, but these are always my first go to tools to start an assessment.
Follow the series!
Stay tuned for more posts in the Cool Tools series, where the Raxis penetration testing team will highlight some of their favorite tools and explain how to get started with them.
In the previous post we built a web page and connected it to a SQL server in order to test and learn about SQL injection. In the previous application the website returned data to the web page making it easy to gather information from the database as the info was printed out.
What happens if the web application does not print the data from the SQL query. Well, there are still ways to gather data. SQLi attacks where the results are not displayed are referred to as Blind SQL Injection. Of course, this makes the attack more difficult, but these are by far the most common SQLi vulnerabilities, and attackers don’t stop just because they take extra effort.
One such way is using timing. MySQL servers have a Sleep function which causes the server to sleep for the specified number of seconds. You can use this in conjunction with comparatives allowing the dumping of the database.
A Refresher
We’re using basically the same application as last time, except that this time the application only returns success or failure depending on whether the username and password entered are correct or not. As a side note, a success fail message can be used much the same way, but this blog will discuss timing.
This is the response when the username and password entered were correct:
And this is the response when the username and password did not match:
Now we can login as we did last time by closing the SQL quote and commenting out the rest of the query, but we’ve already gone over that in the first blog in this series. So let’s explore dumping information from the database instead.
The SLEEP function will cause the server to wait for the specified number of seconds before returning the information.
Example from the command line:
As we can see the query takes five seconds to complete.
SUBSTRING() Function
We will need a way to test one character at a time, so we need a way to get one character from the returned info so we can compare it. For this we use SUBSTRING():
SUBSTRING(String, Position, Length)
IF() Statement
This is how we branch in MySQL.
IF(Condition, Value if true, Value if false)
For the Value if true and Value if false we can do more than just add return values. For instance, we can put the SLEEP function right in the IF function.
We can see that, when the condition was true, the server waited for five seconds.
COUNT() Function
There will be times when we need to know how many of a thing we have. For instance, we might need to know how many columns are in a table or how many rows.
Now, in the database I’m using for testing, I know that there are three columns in the users table.
Here is an example using COUNT showing that.
DATABASE() Function
We can get the current database in use by calling the DATABASE() function:
Querying Database Schemas
If, for some reason, you need to pull the databases manually, maybe because one isn’t set or you want to see what else is out there, you can use this query:
SELECT table_schema FROM information_schema.tables WHERE table_schema NOT IN ( 'information_schema', 'performance_schema', 'mysql', 'sys' ) GROUP BY table_schema;
We should note that default databases are removed by the NOT IN() phrase.
Getting Tables
We can query the information_schema database to get tables in a database:
SELECT table_name from INFORMATION_SCHEMA.tables WHERE table_schema='DATABASE';
Getting Columns
We can also query the information_schema database to get the column names in a table:
SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='DATABASE' and TABLE_NAME='TABLE_NAME';
Comparative Queries with the LIKE & BINARY Functions
= does not always mean equal. With the equal sign we can see how a capital A and a lowercase a are equal. Which is not true in a case sensitive language.
To get around this we can use LIKE BINARY to compare. Here we find that a capital A and a lowercase a are not the same:
CAST() Function
Sometimes when comparing things, it helps to cast items to a known type.
Here is an example of casting 9 to a character:
LENGTH() Function
When trying to figure out what a string is, it helps to get the length of the string:
LIMIT & OFFSET Clauses
Given that we are using Blind SQL, we can really only test one thing at a time. This is where limiting the amount of returned data comes in handy with the LIMIT clause.
We can step down the list using the OFFSET clause. Note that we increase the offset to one less than the count as that will be the last item.
Bringing It All Together
Now that we have all the tools we need, let’s put them together and pull info from the database.
Basically, we will check character by character. First thing we would want to find is the database name. We should probably first figure out how long the database name is.
Since we are using conditionals, it might be easier to use the username part of the query, that way we don’t need to have the right password.
First we see if the length is 1. It’s not, as the response comes back in less than five seconds.
Next we try 2, 3, and 4. We find out that 4 is correct, as the application takes longer than five seconds to respond.
Now we need to figure out the letters in much the same way. Now we use the SUBSTRING function to test one letter at a time.
To make things easier, I used Burp Intruder to send the letters automatically, instead of manually.
We find that the letter S takes five seconds to respond. Now we know the first letter is S.
Next step is to test the second character.
And we find that the second letter is Q.
Now, since I created the application, I know the database name is SQLi so let’s move on to getting table names.
First we use some weird wizardry to discover the number of tables in the database, combining several of the functions we saw above:
Here we are getting the count of tables in the SQLi database. We find that there is only one table.
Now let’s get the table name.
Let’s start with getting the length of the table name.
We find that the length of the name is five. With the length we can start grabbing the chars.
Here’s the query we will use.
Basically, we are asking for all the table names for tables in the SQLi database. We grab the first one and then use substring to test one character at a time.
Using Burp Intruder we find the first character is u. Repeating we find that the table name is users.
Note:
When retrieving names with this method, knowing the length is not truly required. When trying to compare to additional characters – say position six in the table name – it will always return false, meaning that the delay will never occur. If all the possible results stay under the delay, we know that we have the entire string. I like the idea of using the length to make sure I don’t miss something, but it’s not absolutely necessary.
Now that we have the table name, it’s time to start getting data from the table itself. First, we need to know how many columns there are in the table.
When using the COUNT function to learn the number of columns, we find that there are three, as that’s when the sever takes more than five seconds to respond. With the number of columns in hand, let’s get those column names.
This is similar to getting the table name but just querying different information.
Here we get the length of the first column name:
And next the column name itself.
Since we are getting information in the same way, this is very repetitive, so I’m going to assume you get the idea and go through this quickly.
As an example, I’ll show how to get the second column’s information, which just means adding an OFFSET to the limit:
Here we get the first letter of the second column:
The second column is password, so, as expected, we find that the first letter is p.
With all the table information, now we just need to start grabbing the data from the table. We can start by seeing how much data is in a table.
Since this is a small test database, there isn’t a lot of data, so we can count the number of items and compare it to numbers we retrieve easily. On larger sets you may have to be more careful or smarter in gathering info. But this is a basic writeup giving the ideas, and I’ll leave that as an exercise to the readers.
With this database, we find that there are only three records in the table:
Now let’s get the first username from the table:
And finally, we get their password:
In Conclusion
Timing attacks, as with all Blind SQLi, take a good deal of time and patience, but the rewards can be discovering credentials to login to the database or sensitive customer information like PII (Personally Identifiable Information) and financial data, like credit card numbers.
As with all injection attacks, the remediation is to always validate user input. Raxis recommends keeping a list of whitelisted characters and deleting other characters before they process against the database. Cleansing data to be certain that user input is always treated as text (and not run as a command) is also key to this process.
Understanding how to perform attacks like these are critical for web and mobile application penetration testers, just as understanding the idea of how they work is key for application developers so that they can build safeguards into their apps to protect their data and their users.
Web applications often need to store data to better serve their customers. An example would be storing customer login information or comments submitted by users on the webpage. There are many ways of storing data for customers, but a popular way is to store the information in a SQL database.
A common and basic use of web applications and SQL databases is to handle user login information and functionality. In many web applications a user submits a username and password into a form. The web application takes the submitted data and searches the database to determine if it’s a valid credential set. If it is, then the web application will log the user in.
SQL Injection
If the web application does not properly handle user input, an attacker might be able to create malicious input that changes the SQL query that performs that login task behind the scenes. Such SQL injection attacks take a lot of manual effort to discover and exploit, but they are a critical part of the web application penetration tests that we perform at Raxis.
This blog explains how an attacker could find and exploit a SQL injection, or SQLi, vulnerability.
Creating an Exploitable Login Webpage
Let’s build a simple web application that asks for a username and password and returns the user’s ID. In order to help show the SQL injection attack, the application will also show the query used and the input from the login page. The SQL code the application uses (with user input parameters filled in) can be seen at the bottom of each screenshot.
First let’s take a look at the results for the admin logging in normally.
We can see that the user entered the admin username and password. We also see that our sample PHP web application uses a query where the username and password must match for success. If both match then, for our testing purposes, it returns the user ID which is printed out at the bottom.
Here the admin’s account has the ID of 1.
In the next example, the admin username is entered correctly, but the wrong password is supplied.
Again we can see the information that was input into the application and the query. However, since the password does not match the one in the database, we don’t get the user ID.
Exploiting the Webpage to Login
In the example queries above we saw that the username and password input was passed into the query without modification.
So what happens if we close the single quote around the username?
We see that, even though the correct password was entered, the single quote at the end of the username prevents the query from returning a correct result. In this case we get the same error message as if the username and password were incorrect, but other web applications might crash in different ways indicating there was a backend issue.
In MySQL the # symbol denotes a comment, which makes the database ignore everything after it. Let’s try adding a # after that single quote we just added:
Now we see that, even though we submitted the wrong password, the application considers the SQL query successful and returns the correct user ID. The question is What is happening here?
Let’s take a closer look at the query and the input from the user.
The input in the username field has injected two special SQL characters: the ’ (single quote) and the #.
This changes the query itself because the user input is directly inserted into the query.
The single quote closes the username entry so that the text that comes after is read as part of the SQL query. This means that the # is interpreted as the comment symbol, meaning that the rest of the query is simply ignored.
Basically, that means that the query is now just running as
SELECT id, username, password FROM users WHERE username=’admin’
Since the admin user exists, we get the successful result of the user id of 1, even with the wrong password.
This also means that we can login as any user, provided that we know their username. Just change the admin username to the desired username.
Exploiting the Webpage to Get Data
Now logging in as any user is fun and all, but what else can we do? Well, we can come up with SQL queries that dump information from the database.
Let’s use the UNION operator to inject another SQL query that goes along with the query created by the application. We should note that, since the sample web application only shows one column at a time, we need to switch what we are asking for first so that the application will show us more information.
Here is what happens when we add a UNION SELECT for all the user IDs from the users table to the SQL injection we are entering in the username field:
After asking for the IDs, we enter a new SQL injection and ask for the usernames:
And finally, we perform a SQL injection requesting the passwords:
A Quick Note on Password Hashes
This is a good time to note why using password hashes (instead of saving passwords in plaintext) is a good idea. If password hashes were in use, an attacker would have to crack the passwords in order for them to be useful in a SQL injection attack.
Automated Exploitation with SQLMap
While we can do attacks such as these manually, sometimes, after identifying a vulnerability, it is easier to use tools to exploit it for you. In real life scenarios, SQLi attacks don’t return the SQL statement, and it takes some trial and error to discover how the application is reacting to our input.
My go-to tool for SQL injection attacks once I find a sign of them is always SQLMap. Here is a screenshot of SQLMap dumping the users table from the SQLi database the web application above uses.
SQLMap makes it a lot easier to dump information from a database when an application is susceptible to SQLi attacks. While here it dumped the same information we just did, it is capable of finding every table and column and dumping everything.
More Than SQLi
We should also note there are other ways of getting information from a database, including timing attacks in which a database waits to respond if something is true while responding quickly if it’s not. Timing attacks allow us to guess what input is valid and what is not valid. Maybe we will take a look at a timing attack example in another post.
In Conclusion
Now that we have a basic understanding of what SQL injection is and the types of exploits that can be done with it, my next posts in this series will go into specific attacks and how to perform them.
As always, remember that these tutorials are guides for penetration testers and people looking to understand their penetration test results better. Attempting these attacks on any sites that don’t belong to you or where you don’t have legal documentation granting you access to perform ethical penetration testing is illegal and punishable under law.
In a Windows domain, devices have an msDS-AllowedToActOnBehalfOfOtherIdentitity attribute. Per Microsoft, “this attribute is used for access checks to determine if a requestor has permission to act on the behalf of other identities to services running as this account.” In this blog, we will exploit this feature to gain administrative access to a target system in a Resource Based Constrained Delegation (RBCD) attack.
First we need to have control of an account with an SPN (Service Principal Name). The easiest way to do this for our test is to create a machine account. By default any non-admin user can create up to 10 machine accounts, but this value is set by the MachineAccountQuota. You can query this info by using NoPAC scanner.
python3 scanner.py Domain/User -dc-ip DC-IP
Seeing the MachineAccountQuota above 0 (again default is 10), any user can create a machine account. I used impacket’s addcomputer.py script.
After configuring the attribute, I used impacket’s getST.py script to get a Kerberos ticket where we impersonate the administrator user on that device. In this case make sure to use the created machine account to login.
Note: When using the tickets, make sure the target isn’t an IP address but rather the domain name (i.e. lab1.ad.lab). You can use the target-ip flag to point to the right computer if names don’t resolve. I don’t want to admit how long it took me to figure that out.
Playing around with RBCD
Certipy has the ability to access an LDAP shell with a PFX certificate. Say there is web enrollment enabled. As we discussed in the past, you can force the server to authenticate to you then relay it to web enrollment.
certipy relay -ca CA-IP
After a successful relay you can use the saved certificate to access the LDAP shell.
Once in the LDAP shell you can set up the RBCD attack with the set_rbcd command where the first argument is the target device and the second is the controlled account.
set_rbcd Target Controlled-Account
After setting up the RBCD, it’s the same as before using getST to get the ticket and run with it.
Next I wanted to try the same thing but against the domain controller. So I setup certipy to get a domain controller certificate, as we’ve previously discussed.
As a note, because it’s a domain controller, the template has to be specified as DomainController, but you can still use it to access an LDAP shell.
I made a new user, protected.user, to show how to add protections within Active Directory to prevent these attacks. Here I successfully exploit RBCD before adding protections.
As expected, it worked.
Now I checked the box that prevents the account from being delegated.
And then I tried again.
This time the attack didn’t work, which is what we were looking for.
Microsoft also has a group called Protected Users which should (based on my understanding) enable protections against this and other attacks. While I’ve been blocked before by that group while performing penetration tests, for some reason, in my lab, adding a user to that group did not actually prevent the attack. I’m not sure why, but it didn’t, hence the method I discovered above to be sure the account is protected.
A Final Note
The end result for RBCD really is just getting administrative access to a machine. It’s a privilege escalation exploit, and it only works on the machine you’re targeting, not across the domain. If you’re on a DC then great. But it’s still a great way for someone to get admin access to a machine in order to try lateral movement or to access info on that machine during a penetration test.
I recently updated the last installment in my AD series – Active Directory Certificate Services (ADCS) Misconfiguration Exploits – with a few new tricks I discovered recently on an engagement. I mentioned that I have seen web enrollment where it does not listen on port 80 (HTTP), which is the default for certipy. I ran into some weird issues with certipy when testing on port 443, and I found that NTLMRelayx.py worked better in that case. As promised, here is a short blog explaining what I did.
This is basically the same thing as using certipy – just a different set of commands. So here we will go through an example and see how it works.
The first part of the command points to the target. Make sure to include the endpoint (/certsrv/certfnsh.asp) as NTLMRelay won’t know that on its own. Also make sure to tell NTLMRelay if the host is HTTP or HTTPS.
The adcs flag tells NTLMRelay that we are attacking ADCS, and the template flag is used to specify the template. This is needed if you are relaying a domain controller or want to target a specific template. However, if you are planning on just relaying machines or users, you can actually leave this part out.
As connections come in, NTLMRelay will figure out on its own whether it’s a user or machine account and request the proper certificate. It does this based on whether the incoming username ends in a dollar sign. If it ends in a dollar sign NTLMRelay requests a machine certificate, if not it requests a user certificate.
Once NTLMRelay gets a successful relay, it will return a large Base64 blob of data. This is a Base64 encoded certificate.
You can take this Base64 blob and save it to a file. Then just decode the Base64 and save that as a PFX certificate file. After that the attack is the same as the certipy attack in my previous blog. Just use the certificate to login.
Note: This blog was last updated 1/23/2024. Updates are noted by date below.
Active Directory Certificate Services (ADCS) is a server role that allows a corporation to build a public key infrastructure. This allows the organization to provide public key cryptography, digital certificates and digital signatures capabilities to the internal domain.
While using ADCS can provide a company with valuable capabilities on their network, a misconfigured ADCS server could allow an attacker to gain additional unauthorized access to the domain. This blog outlines exploitation techniques for vulnerable ADCS misconfigurations that we see in the field.
Tools We’ll Be Using
Certipy: A great tool for exploiting several ADCS misconfigurations.
PetitPotam: A tool that coerces Windows hosts to authenticate to other machines.
Secretsdump (a python script included in Impacket): A tool that dumps SAM and LSA secrets using methods such as pass-the-hash. It can also be used to dump the all the password hashes for the domain from the domain controller.
CrackMapExec: A multi-fasceted tool that, among other things, can dump user credentials while spraying credentials across the network to access more systems.
If an ADCS certificate authority has web enrollment enabled, an attacker can perform a relay attack against the Certificate Authority, possibly escalating privileges within the domain. We can use Certipy to find ADCS Certificate Authority servers by using the tool’s find command. Note that the attacker would need access to the domain, but the credentials of a simple authenticated user is all that is needed to perform the attack.
certipy find -dc-ip {DC IP Address} -u {User} -p {Password}
First, while setting up ADCS in my test environment, I setup a Certificate Authority to use for this testing.
Certipy’s find command also has a vulnerable flag that will only show misconfigurations within ADCS.
The text file output lists misconfigurations found by Certipy. While setting up my lab environment I checked the box for web enrollment. Here we see that the default configuration is vulnerable to the ESC8 attack:
To exploit this vulnerability, we can use Certipy to relay incoming connections to the CA server. Upon a successful relay we will gain access to a certificate for the relayed machine or the user account. But what really makes this a powerful attack is that we can relay the domain controller machine account, effectively giving us complete access to the domain. Using PetitPotam we can continue the attack and easily force the domain controller to authenticate to us.
The first step is to setup Certipy to relay the incoming connections to the vulnerable certificate authority. Since we are planning on relaying a domain controller’s connection, we need to specify the domain controller template.
certipy relay -ca {Certificate Authority IP Address} -template DomainController
Update 1/11/2024: While on an engagement I found that the organization had changed the default certificate templates. They had switched out the DomainController template with another one. So while I could successfully force a Domain Controller to authenticate, I would receive an error when trying to get a DomainController certificate. After a longer time than I care to admit, I used certipy to check the enabled templates and found that DomainController was not one of them. All I had to do was change the template name to match their custom template name. TL;DR: Check the templates if there is an error getting a DomainController certificate.
Now that Certipy is setup to relay connections, we use PetitPotam to coerce the domain controller into authenticating against our server.
python3 PetitPotam.py -u {Username} -p {Password} {Listener IP Address} {Target IP Address}
After Certipy receives the connection it will relay the connection and get a certificate for the domain controller machine account.
We can then use Certipy to authenticate with the certificate, which gives access to the domain controller’s machine account hash.
We can then use this hash with Secretsdump from the impacket library to dump all the user hashes. We can also use the hash with other tools such as CrackMapExec (CME) and smbclient. Basically anything that allows us to login with a username and hash would work. Here we use Secretsdump.
At this point we have complete access to the windows domain.
Update 1/23/2024: I have seen web enrollment where it does not listen on port 80 over HTTP, which is the default for certipy. I tried to use certipy on an engagement where web enrollment was listening only over HTTPS, and I ran into some weird issues. I found that NTLMRelay seems to work better in that situation, so I’ve written a new post detailing that attack.
Exploit 2: ESC3
In order to test additional misconfigurations that Certipy will identify and exploit, I started adding new certificate templates to the domain. While configuring the new template, I checked the option for Supply in the request, which popped up a warning box about possible issues.
Given that I want to exploit possible misconfigurations, I was happy to see it.
Note: If you are testing in your own environment, once you create the template you will need to configure the CA to actually serve it.
After creating and configuring the new certificate template, we use Certipy to enumerate vulnerable templates using the same command we used to start the previous attack. Certipy identified that the new template was vulnerable to ESC3 issue.
Exploiting this issue can allow an attacker to escalate privileges from those of a normal domain user to a domain administrator. The first step to gaining domain administrator privileges is to request a new certificate based on the vulnerable template. We will need access to the domain as a standard user.
certipy req -dc-ip {DC IP Address} -u {Username} -p {Password} -target-ip {CA IP Address} -ca {CA Server Name} -template {Vulnerable Template Name}
After acquiring the new certificate, we can use Certipy to request another certificate, this time a User certificate, for the administrator account.
certipy req -u {Username} -p {Password} -ca {CA Server Name} -target {CA IP Address} -template User -on-behalf-of {Domain\Username} -pfx {Saved Certificate}
With the certificate for the administrator user, we use certipy to authenticate with the domain, giving us access to the administrator’s password hash.
certipy auth -pfx {Saved Administrator Certificate} -dc-ip {DC IP Address}
At this point we have access to the domain as the domain’s Administrator account. Using the tools we’ve previously learned about like CME, we can take complete control of the domain.
crackmapexec smb {Target IP Address} -u {Username} -H {Password Hash}
From this point, we can use the Secretsdump utility to gather user password hashes from the domain, as previously illustrated.
Exploit 3: ESC4
Another vulnerable misconfiguration that can occur is if users have too much control over the certificate templates. First we configure a certificate on my test network that gives users complete control over the templates.
Now we use Certipy to show the vulnerable templates using the same command as we used in the prior exploits.
We can use Certipy to modify the certificate to make it vulnerable to ESC1, which allows a user to supply an arbitrary Subject Alternative Name.
The first step is to modify the vulnerable template to make it vulnerable to another misconfiguration.
certipy template -u {Username} -p {Password} -template {Vulnerable Template Name} -save-old target-ip {CA Server IP Address}
Note that we can use the save-old flag to save the old configuration. This allows us to restore the template after the exploit.
After modifying the template, we can request a new certificate specifying that it is for the administrator account. When specifying the new account use the account@domain format.
certipy req -u {Username} -p {Password} -ca {CA Server Name} -target {CA Server IP Address} -template {Template Name} -upn {Target Username@Domain} -dc-ip {DC IP Address}
Before we get too far, it’s a good idea to restore the certificate template.
After that we can authenticate with the certificate, again gaining access to the administrator’s hash.
certipy auth -pfx {Saved Certificate} -dc-ip {DC IP Address}
Exploit 4: Admin Control over CA Server
Another route to domain privilege escalation is if we have administrator access over the CA server. In the example lab I am just using a domain administrator account, but in a real engagement this access can be gained any number of ways.
If we have administrator access over the CA server, we can use the certificate to back everything up including the private keys and certificates.
certipy ca -backup -ca {CA Server Name} -u {Username} -p {Password} -dc-ip {DC IP Address}
After backing up the CA server up we can use Certipy to forge a new certificate for the administrator account. In a real engagement the domain information would have to be changed.
certipy forge -ca-pfx {Name of Backup Certificate} -upn {Username@Domain} -subject 'CN=Administrator,CN=Users,DC={Domain Name},DC={Domain Top Level}'
After forging the certificate, we can use it to authenticate, again giving us access to the user’s NTLM password hash.
certipy auth -pfx {Saved Certificate} -dc-ip {DC IP Address}
Now that we setup an AD test environment in my last post, we’re ready to try out broadcast attacks on our vulnerable test network.
In this post we will learn how to use tools freely available for use on Kali Linux to:
Discover password hashes on the network
Pivot to other machines on the network using discovered credentials and hashes
Relay connections to other machines to gain access
View internal file shares
For the attacker machine in my lab, I am using Kali Linux. This can be deployed as a virtual machine on the Proxmox server that we setup in my previous post or can be a separate machine as long as the Active Directory network is reachable.
MiTM6 will pretend to be a DNS server for a IPv6 network. By default Windows prefers IPv6 over IPv4 networks. Most places don’t utilize the IPv6 network space but don’t have it disabled in their Windows domains. Therefore, by advertising as a IPv6 router and setting the default DNS server to be the attacker, MiTM6 can spoof DNS entries allowing for man in the middle attacks. A note from their GitHub even mentions that it is designed to run with tools like ntlmrelayx and responder.
Responder will listen for broadcast name resolution requests and will respond to them on its own. It also has multiple servers that will listen for network connections and attempt to get user computers to authenticate with them, providing the attacker with their password hash. There is more to the tool than what is covered in this tutorial, so check it out!
CME is a useful tool for testing windows computers on the domain. There are many functions within CME that we won’t be discussing in this post, so I definitely recommend taking a deeper look! In this post we are using CME to enumerate SMB servers and whether SMB message signing is required and also to connect to and perform post exploitation activities.
First we will use CME to find all of the SMB servers on the AD network (10.80.0.0/24) and additionally to find those servers which do not require message signing. It saves those which don’t to the file name relay.lst.
Now we’re ready to start ntlmrelayx to relay credentials:
Ntlmrelayx is a tool that listens for incoming connections (mostly SMB and HTTP) and will, when one is received, relay (think forwarding) the connection/authentication to another SMB server. These other SMB servers are those that were found earlier by CME with the –gen-relay-list flag, so we know they don’t require message signing. Note that the smb2support flag just tells ntlmrelayx to setup a SMBv2 server.
Almost immediately we start getting traffic over HTTP:
Running the Attack
So far the responder, mitm6 and ntlmrelayx screens just show the initial starting of the program. Not much is actually happening in any of them. The CME screen is just showing the usage to gather SMB servers that don’t require message signing.
To help things along with our demo, we can force one of the computers on the network to attempt to access a share that doesn’t exist.
While a user looking for a share that doesn’t exist is not needed for this attack, it’s a quick way to skip waiting for an action to occur automatically. Many times on corporate networks, machines will mount shares automatically or users will need a share at some point allowing an attacker to poison the request them. If responder is the first to answer, our attack works, but, if not, the attack doesn’t work in that instance.
Responder captures and poisons the response so that the computer connects to ntlmrelayx, which is still running in the background.
Below we see where responder hears the search for “newshare” and responds with a fake/poisoned response saying that the attacker’s machine is in fact the host for “newshare.” This causes the victim machine to connect to ntlmrelayx which then relays the connection to another computer that doesn’t require message signing. We don’t need to see or crack a user password hash since we are just acting as a man in the middle (hence MiTM) and relaying the authentication from an actual user to another machine.
In this case the user on the Windows machine who searched for “newshare” turns out to be an administrator over some other machines, particularly the machine that ntlmrelayx relayed their credentials to. This means that ntlmrelayx now has administrator access to that machine.
The default action when ntlmrelayx has admin rights is to dump the SAM database. The SAM database holds the username and password hashes (NTLM) for local accounts to that computer. Due to how Windows authentication works, having the NTLM hash grants access as if we had the password. This means we can login to this computer at any time as the local administrator WITHOUT cracking the hash. While NTLM hashes are easy to crack, this speeds up our attack.
If other computers on the network share the same local accounts, we can then login to those computers as the admin as well. We could also use CME to spray the local admin password hash to check for credential reuse. Keep in mind that the rights and access we get to a server all depends on the rights of the user we are pretending to be. In pentests, we often do not start with an admin user and need to find ways to pivot from our initial user to other users with more access until we gain admin access.
The following screenshot shows ntlmrelayx dumping all of the local SAM password hashes on one device on our test network:
While getting the local account password hashes and and gaining access to new machines is a great attack, ntlmrelayx has more flags and modes that allow for other attacks and access. Let’s continue to explore these.
Playing around with –interactive
Ntlmrelayx has a mode that will create new TCP sockets that will allow for an attacker to interact with the created SMB connections after a successful relay. The flag is –interactive.
When the relay is successful a new TCP port is opened. We can connect to it with Netcat:
We can now interact with the host and the shares that are accessible to the user who is relayed.
nc 127.0.0.1 11000
nc 127.0.0.1 11001
Playing around with -SOCKS
With a successful relay ntlmrelayx can create a proxy that we can use with other tools to authenticate to other servers on the network. To use this proxy option ntlmrelayx has the -socks flag.
Here we use ntlmrelayx with the -socks flag to use the proxy option:
Below we see another user has an SMB connection relayed to an SMB server. With the proxy option ntlmrelayx sets up a proxy listener locally. When a new session is created (i.e. a user’s request is relayed successfully) it is added to the running sessions. Then, by directing other tools to the proxy server from ntlmrelayx, we can use these tools interact with these sessions.
In order to use this feature we need to set up our proxychains instance to use the proxy server setup by ntlmrelayx.
The following screen shows the proxychains configuration file at /etc/proxychains4.conf. Here we can see that, when we use the proxychains program, it is going to look for a socks4 proxy at localhost on port 1080. Proxychains is another powerful tool that can do much more than this. I recommend taking a deeper look.
Once we have proxychains set up, we can use any program that logs in over SMB. All we need is a user that has an active session. We can view active sessions that we can use to relay by issuing the socks command on ntlmrelayx:
In this example I have backup.admin session for each of the other 2 computers. Let’s use secretsdump from impacket’s library to gather hashes from the computer.
When the program asks for a password we can supply any text at all, as ntlmrelayx will handle the authentication for us and dump the hashes.
Since I am using a private test lab, the password for backup.admin is “Password2.” Here is an example of logging in with smbclient using the correct password:
Using proxychains to proxy the request through ntlmrelayx, we can submit the wrong password and still login successfully to see the same information:
Next Steps
All of the tools we discussed are very powerful, and this is just a sampling of what they can be used for. At Raxis we use these tools on most internal network tests. If you’re interested in a pentesting career, I highly recommend that you take a deeper look at them after performing the examples in this tutorial.
I hope you’ll join me next time when I discuss Active Directory Certificate Services and how to exploit them in our test AD environment.
Lead Pentester Andrew Trexler walks us through creating a simple AD environment.
Whether you use the environment to test new hacks before trying them on a pentest, or you use it while learning to pentest and study for the OSCP exam, it is a useful tool to have in your arsenal.
The Basics
Today we’ll go through the steps to set up a Windows Active Directory test environment using Proxmox to virtualize the computers. In the end, we’ll have a total of three connected systems, one Domain Controller and two other computers joined to the domain.
Setting up the Domain Controller (DC)
The first step is to setup a new virtualized network that will contain the Windows Active Directory environment. Select your virtualization server on the left:
This is a Windows based environment, but we’re using a Linux hypervisor to handle the underlying network architecture, so under System, select Network, and then create a Linux Bridge, as shown in Figures 2 and 3:
After setting up the network, we provision a new virtual machine where we will install Windows 2019 Server. Figure 4 shows the final configuration of the provisioned machine:
The next step is to install Windows 2019 Server. While installing the operating system make sure to install the Desktop Experience version of the operating system. This will make sure a GUI is installed, making it easier to configure the system.
Now that we have a fresh install, the next step is to configure the domain controller with a static IP address. This will allow the server to function as the DHCP server. Also make sure to set the same IP as the DNS server since the system will be configured later as the domain’s DNS server.
In order to make things easier to follow and understand later, let’s rename the computer to DC1 since it will be acting as our domain controller on the Active Directory domain.
Next, configure the system as a domain controller by using the Add Roles and Features Wizard to add the Active Directory Domain Services and DNS Server roles. This configuration will allow the server to fulfill the roles of a domain controller and DNS server.
After the roles are installed, we can configure the server and provision the new Active Directory environment. In this lab we will use the domain ad.lab. Other than creating a new forest and setting the name, the default options will be fine.
Setting Up the DHCP Service
The next step is to configure the DHCP service. Here we are using a portion of the 10.80.0.0/24 network space, leaving enough addresses available to accommodate static IP addressing where necessary.
There is no need for any exclusions on the network, and we will set the lease to be valid for an entire year.
Adding a Domain Administrator and Users
Additional configuration is now required within the domain. Let’s add a new domain administrator and some new domain users. Their names and passwords can be anything you want, just make sure to remember them.
First we create the Domain Administrator (DA):
Here we also make this user an Enterprise Admin (EA) by adding them to the Enterprise Admins group:
Next we will add a normal user to the domain:
Creating Windows PC
At this point we should have a functional Active Directory domain with active DHCP and DNS services. Next, we will setup and configure two other Windows 10 machines and join them to the domain.
The first step is to provision the resources on the Proxmox server. Since our test environment requires only moderate resources, we will only provision the machines with two processor cores and two gigabytes of RAM.
Then we install Windows 10 using the default settings. Once Windows is installed, we can open the Settings page and join the system to the ad.lab domain, changing the computer name to something easy to remember if called for.
Adding the system to the domain will require us to enter a domain admin’s password. After a reboot we should be able to login with a domain user’s account.
SMB Share
At this point, there should be three computers joined to the Active Directory domain. Using CrackMapExec, we can see the SMB server running on the domain controller but no other systems are visible via SMB. So let’s add a new network share. Open Explorer.exe, select Advance Sharing, and share the C drive.
I don’t recommend sharing the entire drive in an environment not used for testing, as it’s not secure: the entire contents of the machine would be visible. Since this is a pentest lab environment, though, this is exactly what we are looking for.
Creating the share resulted in the system exposing the SMB service to the network. In Figure 20 we verified this by using CrackMapExec to enumerate the two SMB servers:
Conclusion
At this point, our environment should be provisioned, and we are ready to test out different AD test cases, attacks, and other shenanigans. This environment is a great tool for ethically learning different exploits and refining pentesting techniques. Using a virtual infrastructure such as this also provides rollback capability for running multiple test cases with minimal downtime.
I hope you’ll come back to see my next posts in this series, which will show how to use this environment to test common exploits that we find during penetration testing.
GraphQL is a query language inspired by the structure and functionality of online data storage and collaboration platforms Meta, Instagram, and Google Sheets. This post will show you how to take advantage of one of its soft spots.
Development
Facebook developed GraphQL in 2012 and then it became open source in 2015. It’s designed to let application query data and functionality be stored in a database or API without having to know the internal structure or functionality. It makes use of the structural information exchanged between the source and the engine to perform query optimization, such as removing redundancy and including only the information that is relevant to the current operation.
To use GraphQL, since it is a query language (meaning you have to know how to code with it), many opt to use a platform to do the hard work. Companies like the New York Times, PayPal, and even Netflix have dipped into the GraphQL playing field by using Apollo.
Apollo
Apollo Server is an open-source, spec-compliant tool that’s compatible with any GraphQL client. It builds a production-ready, self-documenting GraphQL API that can use data from any source.
Apollo GraphQL has three primary tools and is well documented.
Client: A client-side library that helps you digest a GraphQL API along with caching the data that’s received.
Server: A library that connects your GraphQL Schema to a server. Its job is to communicate with the backend to send back responses based off the request of the client.
Engine: Tracks errors, gathers stats, and performs other monitoring tasks between the Apollo client and the Apollo server.
(We now understand that GraphQL is query language and Apollo is spec-compliant GraphQL server.)
A pentester’s perspective
What could be better than fresh, new, and developing technology? Apollo seems to be the king of the hill, but the issue here is the development of Apollo’s environment is dynamic and fast. Its popularity is growing along with GraphQL, and there seems to be no real competition on the horizon, so it’s not surprising to see more and more implementations. The most difficult part for developers is having proper access controls for each request and implementing a resolver that can integrate with the access controls. Another key point is the constant new build releases with new bugs.
Introspection
Batch attacks, SQLi, and debugging operations that disclose information are known vulnerabilities when implementing GraphQL. This post will focus on Introspection.
Introspection enables you to query a GraphQL server for information about the schema it uses. We are talking fields, queries, types, and so on. Introspection is mainly for discovery and as a diagnostic tool during the development phase. In a production system you usually don’t want just anyone knowing how to run queries against sensitive data. When they do, they can take advantage of this power. For example, the following field contains interesting information that can be queried by anyone on a GraphQL API in a production system with introspection enabled:
Let’s try it
One can obtain this level of information in a few ways. One way is to use Burp Suite and the GraphQL Raider plugin. This plugin allows you to isolate the query statement and experiment on the queries. For example, intercepting a post request for “/graphql”, you may see a query in the body, as shown below:
Using Burp Repeater with GraphQL we can change the query located in the body and execute an Introspection query for ‘name’ and see the response.
Knowing GraphQL is in use, we use Burp Extension GraphQL Raider to focus just on queries. Here we are requesting field names in this standard GraphQL request, but this can be changed to a number of combinations for more results once a baseline is confirmed.
This request is checking the ‘schema’ for all ‘types’ by ‘name’ . This is the response to that query on the right.
Looking further into the response received, we see a response “name”: “allUsers”. Essentially what is happening is we are asking the server to please provide information that has “name” in it. The response gave a large result, and we spot “allUsers”. If we queried that specific field, it would likely provide all the users.
The alternative would be to use CURL. You can perform the same actions simply by placing the information in a curl statement. So the same request as above translated over for Curl would be similar to:
You could opt to do this in the browser address bar as well, but that can be temperamental at times. So you can see how easy it is to start unraveling the treasure trove of information all without any authentication.
Even more concerning are the descriptive errors the system provides that can help a malicious attacker succeed. Here we use different curl statement to the server. This is the same request except that the query is for “system.”
When the request cannot be fulfilled, the server tries its best to recommend a legitimate field request. This allows the malicious attacker to formulate and build statements one error at a time if needed:
Pentest ToolBox
Ethical hackers would be wise to add this full request to their toolbox as it should provide a full request that provides a long list of objects, fields, mutations, descriptions, and more:
{__schema{queryType{name}mutationType{name}subscriptionType{name}types{...FullType}directives{name description locations args{...InputValue}}}}fragment FullType on __Type{kind name description fields(includeDeprecated:true){name description args{...InputValue}type{...TypeRef}isDeprecated deprecationReason}inputFields{...InputValue}interfaces{...TypeRef}enumValues(includeDeprecated:true){name description isDeprecated deprecationReason}possibleTypes{...TypeRef}}fragment InputValue on __InputValue{name description type{...TypeRef}defaultValue}fragment TypeRef on __Type{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name ofType{kind name}}}}}}}}
Ethical hackers, may want to add these to their directory brute force attacks as well:
/graphql
/graphiql
/graphql.php
/graphql/console
Conclusion
Having GraphQL introspection in production might expose sensitive information and expand the attack surface. Best practice recommends disabling introspection in production unless there is a specific use case. Even in this case, consider allowing introspection only for authorized requests, and use a defensive in-depth approach.
You can turn off introspection in production by setting the value of the introspection config key on your Apollo Server instance.
Although this post only addresses Introspection, GraphQL/Apollo is still known to be vulnerable to the attacks I mentioned at the beginning – batch attacks, SQLi, and debugging operations that disclose information – and we will address those in subsequent posts. However, the easiest and most common attack vector is Introspection. Fortunately, it comes with an equally simple remedy: Turn it off.
In this article Raxis, a top tier provider in cybersecurity penetration testing, demonstrates how a remote shell can be obtained on a target system using a Log4j open source exploit that is available to anyone.
Introduction
This critical vulnerability, labeled CVE-2021-44228, affects a large number of customers, as the Apache Log4j component is widely used in both commercial and open source software. In addition, ransomware attackers are weaponizing the Log4j exploit to increase their reach to more victims across the globe.
Our demonstration is provided for educational purposes to a more technical audience with the goal of providing more awareness around how this exploit works. Raxis believes that a better understanding of the composition of exploits it the best way for users to learn how to combat the growing threats on the internet.
Log4j Exploit Storyboard
The Apache Log4j vulnerability, CVE-2021-44228 (https://nvd.nist.gov/vuln/detail/CVE-2021-44228), affects a large number of systems, and attackers are currently exploiting this vulnerability for internet-connected systems across the world. To demonstrate the anatomy of such an attack, Raxis provides a step-by-step demonstration of the exploit in action. Within our demonstration, we make assumptions about the network environment used for the victim server that would allow this attack to take place. There are certainly many ways to prevent this attack from succeeding, such as using more secure firewall configurations or other advanced network security devices, however we selected a common “default” security configuration for purposes of demonstrating this attack.
Victim Server
First, our victim server is a Tomcat 8 web server that uses a vulnerable version of Apache Log4j and is configured and installed within a docker container. The docker container allows us to demonstrate a separate environment for the victim server that is isolated from our test environment. Our Tomcat server is hosting a sample website obtainable from https://github.com/cyberxml/log4j-poc and is configured to expose port 8080 for the vulnerable web server. No other inbound ports for this docker container are exposed other than 8080. The docker container does permit outbound traffic, similar to the default configuration of many server networks.
Note, this particular GitHub repository also featured a built-in version of the Log4j attack code and payload, however, we disabled it for our example in order to provide a view into the screens as seen by an attacker. We are only using the Tomcat 8 web server portions, as shown in the screenshot below.
Next, we need to setup the attacker’s workstation. Using exploit code from https://github.com/kozmer/log4j-shell-poc, Raxis configures three terminal sessions, called Netcat Listener, Python Web Server, and Exploit, as shown below.
Netcat Listener, Port 9001
The Netcat Listener session, indicated in Figure 2, is a Netcat listener running on port 9001. This session is to catch the shell that will be passed to us from the victim server via the exploit.
Python Web Server, Port 80
The Python Web Server session in Figure 3 is a Python web server running on port 80 to distribute the payload to the victim server.
Exploit Code, Port 1389
The Exploit session, shown in Figure 4, is the proof-of-concept Log4j exploit code operating on port 1389, creating a weaponized LDAP server. This code will redirect the victim server to download and execute a Java class that is obtained from our Python Web Server running on port 80 above. The Java class is configured to spawn a shell to port 9001, which is our Netcat listener in Figure 2.
Execute the Attack
Now that the code is staged, it’s time to execute our attack. We’ll connect to the victim webserver using a Chrome web browser. Our attack string, shown in Figure 5, exploits JNDI to make an LDAP query to the Attacker’s Exploit session running on port 1389.
The attack string exploits a vulnerability in Log4j and requests that a lookup be performed against the attacker’s weaponized LDAP server. To do this, an outbound request is made from the victim server to the attacker’s system on port 1389. The Exploit session in Figure 6 indicates the receipt of the inbound LDAP connection and redirection made to our Attacker’s Python Web Server.
The Exploit session has sent a redirect to our Python Web Server, which is serving up a weaponized Java class that contains code to open up a shell. This Java class was actually configured from our Exploit session and is only being served on port 80 by the Python Web Server. The connection log is show in Figure 7 below.
The last step in our attack is where Raxis obtains the shell with control of the victim’s server. The Java class sent to our victim contained code that opened a remote shell to our attacker’s netcat session, as shown in Figure 8. The attacker now has full control of the Tomcat 8 server, although limited to the docker session that we had configured in this test scenario.
Conclusion
As we’ve demonstrated, the Log4j vulnerability is a multi-step process that can be executed once you have the right pieces in place. Raxis is seeing this code implemented into ransomware attack bots that are searching the internet for systems to exploit. This is certainly a critical issue that needs to be addressed as soon as possible, as it is a matter of time before an attacker reaches an exposed system.