Having completed the OSCP back in November, I have decided to delve a bit into Application Security just as a way to learn a different side of things. After completing both the Security Engineer and DevSecOps paths on TryHackMe, I started what has turned out to be a series on Free Open-Source Software tools for application security. In this case, I looked a little bit into both Semgrep and SonarQube and decided to focus on the latter. Per their website:
Sonar’s static application security testing (SAST) engine detects security vulnerabilities in your code so they can be eliminated before you build and test your application.
I tried a few different ways to do this, but the easiest turned out to be with Docker. I’ve only ever worked with Docker in one-off lab situations, but this process was simple enough that I expect I will be using it more in the future. From reference I am using an Ubuntu VM on my ARM machine. To get started, you will need 5 things:
In order:
From docker’s documentation, you can set up docker’s apt
repository with these commands:
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
Then you can install the necessary packages with: sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
. Test that it worked with:sudo docker run hello-world
. You should get a message from the output that says: “This message shows that your installation appears to be working properly.” At this point docker is installed.
Per the apache website, Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project’s build, reporting and documentation from a central piece of information. In this case this is necessary for the kind of project I will be testing on, but there will be other options (Gradle, .NET, or other) as we’ll explain later. You can install it with sudo apt-get install maven
.
Next we need a java runtime environment. This may depend on your project, but I am going to use JDK 21 as this is the current Long Term Stable (LTS) release. It can be installed with sudo apt-get install openjdk-21-jdk
.
Because we installed the docker apt repository, we can simply pull the latest community release of sonarqube with sudo docker pull sonarqube:community
. Easy peasy. Now that we have the docker image, we also need to create a docker-compose.yml
file which will tell docker what to do with it. Here is an example:
services:
sonarqube:
image: sonarqube:community
ports:
- "9000:9000"
environment:
- SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonar
- SONAR_JDBC_USERNAME=sonar
- SONAR_JDBC_PASSWORD=sonar
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
- sonarqube_logs:/opt/sonarqube/logs
depends_on:
- db
db:
image: postgres:12
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
- POSTGRES_DB=sonar
volumes:
- postgresql:/var/lib/postgresql
- postgresql_data:/var/lib/postgresql/data
volumes:
sonarqube_data:
sonarqube_extensions:
sonarqube_logs:
postgresql:
postgresql_data:
This specifies that port to use (9000) as well as the postgres version (12) and credentials (sonar:sonar). You will want to run docker from the same directory that this YAML file is located in.
git clone https://github.com/WebGoat/WebGoat.git
.We can get started with docker compose up -d
. This will start the SonarQube service which you can access on port http://127.0.0.1:9000 in your browser.
When you access the service you will be prompted for the default credentials (admin:admin) and then made to change them. Then you will be asked how to create the project. In my case, I am selecting a local project.
Again, I am using the WebGoat project I have downloaded.
Default settings.
Locally.
Next we need to generate a token for the project. SonarQube will then use that token to output the command needed to run. After you name it and press generate, you can simply run the listed commands in your project directory and view the results.
After running the commands, we can go back to the dashboard, click into the project, and view our results:
There’s a lot to break down here, so I’m not going to go through all of it, but we can take a look at what all is there. It looks like there are 19 Security issues and 39 Reliability issues. Let’s take a look at some of the security issues to get started.
One thing to note is that it looks like all 19 of these are considered to be of High
severity. If these are true positives, then we definitely need to look at them carefully. Let’s try a few:
I click into the first one, which says “Revoke and change this password, as it is compromised.” SonarQube shows us the block of code in question here:
It looks to me like they have flagged the string “password” regardless of context. In this case, it looks like the “password” is the name of the password for a login function. These are not hardcoded credentials, they are just what the login page takes as input from the user. Maybe there are less obvious things to call the username and password fields, but discovering the names of these parameters is trivial, and it certainly should not be considered a High
severity vulnerability. To be especially sure, I tried the username:password combo myself and was unable to login.
Remediation: I am marking it as a false positive and moving on to the next one.
The next High
security issue says “Don’t use the default ‘PasswordEncoder’ relying on plain-text.” Essentially the concern here is that SonarQube believes the passwords are stored in plaintext. If an attacker gains access to the database, then they are able to access the unhashed passwords and easily compromise the accounts.
After doing some research and looking around in the code, I can see that this is not a false positive and is in fact a High
severity issue. The passwords are encoded further down the file with something called NoOpPasswordEncoder
which is explicitly designed to do no encoding. In this case, SonarQube has correctly identified a problem that must be remediated because the configureGlobal
method does not have a passwordEncoder. In fact, if we view the rest of the file, we see this:
@Bean
public NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
}
So we can see that the passwordEncoder()
method is using NoOpPasswordEncoder
which provides no protection.
Remediation:
There are a couple of things we need to do here. We need to define set passwordEncoder()
method to use a proper encoder. We can use BCrypt for example:
@Bean
public PasswordEncoder passwordEncoder() {
return BCryptPasswordEncoder();
}
}
We also need to require that the configureGlobal()
method specifies a password encoder, which we’ve just defined. We can do this by adding an additional line to the flagged code like so:
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder()); // Additional line }
But in order for these to work, we need to make sure to import them at the top. It’s not shown in the code snippet, but the 45th line says: import org.springframework.security.crypto.password.NoOpPasswordEncoder;
, and we need to change. We also need to import BCrypt. So the imports should now look like this:
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
After that we should be good. I’m going to mark it as Confirmed and assign to the Administrator as I haven’t added any new users.
On to the next example!
The next Security issue is “Use secure ‘PasswordEncoder’ implementation.”
This issue is actually related to the previous issue but is a different section of code being flagged. In fact, when we make the three suggested changes to the previous issue, this flagged section is one of the changes we are making. Instead of this:
@Bean
public NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
}
We will do this:
@Bean
public PasswordEncoder passwordEncoder() {
return BCryptPasswordEncoder();
}
}
Remediation: We will already be making the necessary changes as part of the remediations steps for issue 2, but they are listed again above. In this case, I will mark it as Confirmed and assign it to the Administrator once again.
That will do it for now. In real life it’s important to go through all of these vulnerabilities of course, but I think this is good practice for how to stand up the SonarQube application and perform static application security testing on it. Now that I’ve done SCA and SAST, maybe soon I’ll try to run a DAST tool or vulnerability scanner against the application. I believe in those cases, I’ll need to actually stand up the application, but that should be simple enough.