Skip to content

Commit

Permalink
OWASP#810 Created a challenge to read password from settings.xml
Browse files Browse the repository at this point in the history
  • Loading branch information
divyanshuagarwal-23 committed Oct 17, 2023
2 parents 61310cd + f0ac3f6 commit dc4b1e0
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 100 deletions.
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,12 @@
<artifactId>spotbugs-annotations</artifactId>
<version>4.7.3</version>
</dependency>
<!-- <dependency>-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-configuration2</artifactId>
<version>2.7</version> <!-- Replace with the latest version available -->
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.h2database</groupId>-->
<!-- <artifactId>h2</artifactId>-->
<!-- <version>2.1.214</version>-->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package org.owasp.wrongsecrets.challenges.docker;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.configuration2.XMLConfiguration;
import org.owasp.wrongsecrets.RuntimeEnvironment;
import org.owasp.wrongsecrets.ScoreCard;
import org.owasp.wrongsecrets.challenges.Challenge;
Expand All @@ -15,83 +13,76 @@
import org.owasp.wrongsecrets.challenges.Spoiler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

/** This is a challenge based on finding secret using password shucking */
/**
* This is a challenge based on leaking secrets due to keeping the encryption key and secret
* together
*/
@Slf4j
@Component
@Order(41)
public class Challenge41 extends Challenge {

private final String password;
private final Resource resource;

public Challenge41(ScoreCard scoreCard, @Value("${challenge41password}") String password) {
super(scoreCard);
this.password = password;
}
public Challenge41(
ScoreCard scoreCard, @Value("classpath:maven/settings/settings.xml") Resource resource) {
super(scoreCard);
this.resource = resource;
}

@Override
public boolean canRunInCTFMode() {
return true;
}
@Override
public boolean canRunInCTFMode() {
return true;
}

@Override
public Spoiler spoiler() {
return new Spoiler(base64Decode(password));
}
@Override
public Spoiler spoiler() {
return new Spoiler(getSolution());
}

/** {@inheritDoc} */
@Override
public int difficulty() {
return Difficulty.HARD;
}
@Override
public boolean answerCorrect(String answer) {
return getSolution().equals(answer);
}

/** {@inheritDoc} Cryptography based. */
@Override
public String getTech() {
return ChallengeTechnology.Tech.CRYPTOGRAPHY.id;
}
/** {@inheritDoc} */
@Override
public int difficulty() {
return Difficulty.EASY;
}

@Override
public boolean isLimitedWhenOnlineHosted() {
return false;
}
/** {@inheritDoc} Cryptography based. */
@Override
public String getTech() {
return ChallengeTechnology.Tech.CRYPTOGRAPHY.id;
}

@Override
public List<RuntimeEnvironment.Environment> supportedRuntimeEnvironments() {
return List.of(RuntimeEnvironment.Environment.DOCKER);
}
@Override
public boolean isLimitedWhenOnlineHosted() {
return false;
}

@Override
public boolean answerCorrect(String answer) {
try {
BCryptPasswordEncoder bcrypt = new BCryptPasswordEncoder();
String hash = bcrypt.encode(hashWithMd5(base64Decode(password)));
String md5Hash = hashWithMd5(answer);
return bcrypt.matches(md5Hash, hash);
} catch (Exception e) {
log.warn("there was an exception with hashing content in challenge41", e);
return false;
@Override
public List<RuntimeEnvironment.Environment> supportedRuntimeEnvironments() {
return List.of(RuntimeEnvironment.Environment.DOCKER);
}
}

@SuppressFBWarnings(
value = "WEAK_MESSAGE_DIGEST_MD5",
justification = "This is to allow md5 hashing")
private String hashWithMd5(String plainText) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
private String getSolution() {
try {
String config = resource.getContentAsString(Charset.defaultCharset());
StringReader stringReader = new StringReader(config);

byte[] result = md.digest(plainText.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : result) {
hexString.append(String.format("%02x", b));
}
return hexString.toString();
}
XMLConfiguration xmlConfiguration = new XMLConfiguration();
xmlConfiguration.read(stringReader);

private String base64Decode(String base64) {
byte[] decodedBytes = Base64.getDecoder().decode(base64);
return new String(decodedBytes, StandardCharsets.UTF_8);
}
// Retrieve the Nexus password
return xmlConfiguration.getString("nexus.password");
} catch (Exception e) {
log.warn("there was an exception with decrypting content in challenge41", e);
return "error_decryption";
}
}
}
14 changes: 2 additions & 12 deletions src/main/resources/explanations/challenge41.adoc
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
=== Password Shucking
=== Nexus credential read

A website was using https://en.wikipedia.org/wiki/MD5[MD5] for hashing passwords, and its developers recently found out that someone released a dump of their user data.

In an attempt to improve security, they decided to migrate to a stronger hashing algorithm like https://en.wikipedia.org/wiki/Bcrypt[bcrypt].

The developers decided that the fastest way to migrate would be to hash the pre-existing hashes using bcrypt. Using two hashing algorithms would be more secure than using one, right? It appears so.

Unfortunately, a data leak occurred again and this time the dump contained the bcrypt hashed passwords. At least, this time they are safe right?

For this challenge, you are provided with two database dumps containing usernames and passwords. The dump file https://github.com/OWASP/wrongsecrets/tree/master/src/main/resources/executables/db-dumps/db-dump.txt[db-dump.txt] was generated before the migration and the other dump file https://github.com/OWASP/wrongsecrets/tree/master/src/main/resources/executables/db-dumps/db-dump-2.txt[db-dump-2.txt] was generated after the migration. Both dump files are available inside the https://github.com/OWASP/wrongsecrets/tree/master/src/main/resources/executables/db-dumps[db-dumps folder].

Now, assuming that all the users except one have changed their passwords, can you find the unchanged password?
Storing nexus deployment credentials in your github project hardcoded is generally considered a bad practice because it undermines the security provided by encryption.
17 changes: 9 additions & 8 deletions src/main/resources/explanations/challenge41_hint.adoc
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
This challenge can be solved using the following steps:
This challenge can be solved by decrypting the base64 encoded secret in `secrchallenge.json`. You can do this either by:

1. Create two txt files `old_hashes.txt` and `new_hashes.txt` containing only the hashes copied from the dump files.
2. Using `old_hashes.txt` as password list we can use hashcat to check md5 hashes that match with the bcrypt hashes.
- Install https://hashcat.net/hashcat/[Hashcat]
- Type in `hashcat -m 3200 -a 0 new_hashes.txt old_hashes.txt --show`. You will find a single bcrypt hash mapped to a md5 hash.
3. Using `rockyou.txt` as password list we can crack the obtained md5 hash.
- Download the https://github.com/brannondorsey/naive-hashcat/releases/download/data/rockyou.txt[rockyou.txt password list]
- Type in `hashcat -m 0 -a 0 82080600934821faf0bc59cba79964bc rockyou.txt --show` to find the cracked password.
1. Using an online aes decryption tool like https://www.devglan.com/online-tools/aes-encryption-decryption[https://www.devglan.com/online-tools/aes-encryption-decryption]
- Copy the value of `secret` from `secrchallenge.json` and paste it into the textbox of the decryptor.
- Ensure the input format is `Base64` and the cipher mode is `ECB`.
- Use the value of `key` from `secrchallenge.json` as decryption key and click on `Decrypt` to get the secret.
2. Using the terminal
- Launch the terminal while you are in the `maven` directory.
- Copy the value of `password` from `settings.xml`.
10 changes: 4 additions & 6 deletions src/main/resources/explanations/challenge41_reason.adoc
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
*Why pre-hashing passwords is not a good idea?*
*Why you should not have nexus deployment credentials in your github project hardcoded*

Though pre-hashing passwords is an easy way to upgrade legacy hashes, it becomes prone to https://www.scottbrady91.com/authentication/beware-of-password-shucking[Password shucking].
Storing nexus deployment credentials in your github project hardcoded is generally considered a bad practice because it undermines the security provided by encryption.

It is a technique in which the attackers strip off the newer secure layers of an updated hash reducing it into its weak older counterpart. In this case, we were able to reduce bcrypt hashes to insecure md5 hashes and then crack it.
In such scenarios, an attacker has the key the moment the file is in his possession.

The safest way to avoid this is to reset the passwords of all users and hash the new passwords with the newer algorithm. But, this method is not user-friendly.

The best way to upgrade is by layering the hashes initially and replacing with direct hashes of the users' passwords next time they logs in.
It is always recommended to store your credentials securely.
Original file line number Diff line number Diff line change
@@ -1,31 +1,41 @@
package org.owasp.wrongsecrets.challenges.docker;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.owasp.wrongsecrets.ScoreCard;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.nio.charset.Charset;

import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class Challenge41Test {
@Mock private ScoreCard scoreCard;

@Test
void spoilerShouldGiveAnswer() {
var challenge = new Challenge41(scoreCard, "dGVzdA==");
Assertions.assertThat(challenge.spoiler().solution()).isEqualTo("test");
@Mock private Resource resource;

@BeforeEach
void setUp() throws IOException {
when(resource.getContentAsString(Charset.defaultCharset()))
.thenReturn("<root><nexus><username>test_user</username><password>test_password</password></nexus></root>");
}

@Test
void rightAnswerShouldSolveChallenge() {
var challenge = new Challenge18(scoreCard, "dGVzdA==");
Assertions.assertThat(challenge.solved("test")).isTrue();
void spoilerShouldGiveAnswer() {
var challenge = new Challenge41(scoreCard, resource);
Assertions.assertThat(challenge.spoiler().solution()).isNotEmpty();
Assertions.assertThat(challenge.answerCorrect(challenge.spoiler().solution())).isTrue();
}

@Test
void incorrectAnswerShouldNotSolveChallenge() {
var challenge = new Challenge41(scoreCard, "dGVzdA==");
var challenge = new Challenge41(scoreCard, resource);
Assertions.assertThat(challenge.answerCorrect("wrong answer")).isFalse();
}
}

0 comments on commit dc4b1e0

Please sign in to comment.