🌐 Java Code Analysis!?!
A detailed write-up of the Web challenge 'Java Code Analysis!?!' from PicoCTF - 2023
📊 Challenge Overview
Category Details Additional Info 🏆 Event PicoGym Event Link 🔰 Category Web 🌐 💎 Points Out of 500 total ⭐ Difficulty 🟡 Medium Personal Rating: 3/10 👤 Author Nandan Desai Profile 🎮 Solves (At the time of flag submission) 4.594 solve rate 📅 Date 11-02-2025 PicoGym 🦾 Solved By mH4ck3r0n3 Team:
📝 Challenge Information
BookShelf Pico, my premium online book-reading service. I believe that my website is super secure. I challenge you to prove me wrong by reading the ‘Flag’ book! Here are the credentials to get you started:
- Username: “user”
- Password: “user”
🎯 Challenge Files & Infrastructure
Provided Files
Files:
🔍 Initial Analysis
First Steps
Initially, the website appears as follows:
Trying to log in with the credentials provided by the challenge
username=user & password=user
, I was able to access the following page:The description also suggests that to obtain the flag, we need to read the book
Flag
, which, as we can see, is locked and can only be accessed by anAdmin
account. This led me to think that there might be some sort of authorization, like a cookie or token, that needs to be forged in order to access the book with privileged access. Going through the attached files, as suggested by the title of the challenge, it’s related toJava
. Below, I will report only the interesting things I found. The first thing that caught my eye was the directorysrc/main/java/io/github/nandandesai/pico/security
. Upon opening it, I discovered thatJWT
is being used:And as we can see, the fields are
userId
,role
, where I imaginerole
will determine the access level to thebooks
. In fact, as expected, in the filesrc/main/java/io/github/nandandesai/pico/security/models/UserAuthority.java
, I found the following lines that suggest the role is associated with the permissions:
1 2
private Integer userId; private String authority; //role
In the file
JwtService.java
, there is a reference to how theJWT
token is formatted and decoded. In the class constructor, theSECRET_KEY
is set, which is used to encrypt the token:
1 2 3 4 5
private final String SECRET_KEY; @Autowired public JwtService(SecretGenerator secretGenerator){ this.SECRET_KEY = secretGenerator.getServerSecret(); }
It uses the
secretGenerator
object with the functiongetServerSecret()
. Let’s take a look at how it’s implemented by opening the fileSecretGenerator.java
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
@Service class SecretGenerator { private Logger logger = LoggerFactory.getLogger(SecretGenerator.class); private static final String SERVER_SECRET_FILENAME = "server_secret.txt"; @Autowired private UserDataPaths userDataPaths; private String generateRandomString(int len) { // not so random return "1234"; } String getServerSecret() { try { String secret = new String(FileOperation.readFile(userDataPaths.getCurrentJarPath(), SERVER_SECRET_FILENAME), Charset.defaultCharset()); logger.info("Server secret successfully read from the filesystem. Using the same for this runtime."); return secret; }catch (IOException e){ logger.info(SERVER_SECRET_FILENAME+" file doesn't exists or something went wrong in reading that file. Generating a new secret for the server."); String newSecret = generateRandomString(32); try { FileOperation.writeFile(userDataPaths.getCurrentJarPath(), SERVER_SECRET_FILENAME, newSecret.getBytes()); } catch (IOException ex) { ex.printStackTrace(); } logger.info("Newly generated secret is now written to the filesystem for persistence."); return newSecret; } } }
It retrieves the
SECRET_KEY
from the fileserver_secret.txt
, but if it doesn’t exist, it generates it as follows:String newSecret = generateRandomString(32)
. If we look closely, the functiongenerateRandomString()
takes anint
as an argument but always returnsreturn "1234";
. So, if theserver_secret.txt
file is not present on the server, the key will be1234
(which we can exploit to forge a token and verify it with the secret key). Later, I found an interesting comment in the filemodels/Role.java
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@Getter @Setter @NoArgsConstructor @Accessors(chain = true) @Entity @Table(name = "roles") public class Role { @Id @Column private String name; @Column private Integer value; //higher the value, more the privilege. By this logic, admin is supposed to // have the highest value }
As we can see, the higher the value of the role, the greater the privileges, so I thought I would need to assign the highest value to gain administrator privileges. Later, in the file
configs/BookShelfConfig.java
, I found the initialization of theDB
with the users:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
Role FreeRole = roleRepository.findById("Free").get(); Role PremiumRole = roleRepository.findById("Premium").get(); Role AdminRole = roleRepository.findById("Admin").get(); /* * Initialize admin and a user * */ User freeUser = new User(); freeUser.setProfilePicName("default-avatar.png") .setRole(FreeRole) .setLastLogin(LocalDateTime.now()) .setFullName("User") .setEmail("user") .setPassword(passwordEncoder.encode("user")); userRepository.save(freeUser); User admin = new User(); admin.setProfilePicName("default-avatar.png") .setRole(AdminRole) .setLastLogin(LocalDateTime.now()) .setFullName("Admin") .setEmail("admin") .setPassword(passwordEncoder.encode("\<redacted>")); userRepository.save(admin);
As we can see, the user
admin
hasemail=admin
androle=Admin
, and alsoid=2
, as it is the second user created in the database. Now that we have all this information, we just need to take theJWT
of theuser
and use it to change the account toadmin
in order to access theFlag
book. I retrieved the token through theNetwork
section of theChromeDevTools
:As we can see, in the request header section, the
authorization
containsBearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiRnJlZSIsImlzcyI6ImJvb2tzaGVsZiIsImV4cCI6MTczOTg5NDI2OCwiaWF0IjoxNzM5Mjg5NDY4LCJ1c2VySWQiOjEsImVtYWlsIjoidXNlciJ9.lE6bz9SmbWdhuazvtYgSvm03kBRp7kTpTHXESXp4HaY
. These are usually also stored inLocalStorage
. In fact, when I tried going to theApplication
section, I found the token there as well:The first thing I did was analyze it with https://jwt.io:
But I am shown the same
token payload
seen in the image above. The additional information I get is the algorithm:
1 2 3 4
{ "typ": "JWT", "alg": "HS256" }
It uses
HS256
. Now that we have everything we need, let’s move on to the exploitation phase.
🔬 Vulnerability Analysis
Potential Vulnerabilities
- JWT Weak Secret
🎯 Solution Path
Exploitation Steps
Initial setup
After understanding the vulnerability and how to exploit it (by trying to forge an admin token with the secret key), we can proceed to the next step.
Exploitation
From all that information, it’s very easy to forge a valid
JWT
with theadmin
user. In fact, all we need to do is directly modify the fields in thedecoded
section of jwt.io.Now, all that’s left is to insert the
token payload
and theforged token
into the local storage, refresh the page (pressF5
), and access theFlag
book.
Flag capture
🛠️ Exploitation Process
Approach
The automatic exploit was not possible for me to write because it didn’t allow me to access the page with the PDF, otherwise, it could have been extracted with
pyPDF2
to get the flag. However, I usedcurl
to extract the PDF with the generated token:
1
curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlIjoiQWRtaW4iLCJpc3MiOiJib29rc2hlbGYiLCJleHAiOjE3Mzk4OTcyNzksImlhdCI6MTczOTI5MjQ3OSwidXNlcklkIjoyLCJlbWFpbCI6ImFkbWluIn0.HtqfQ48AgTGT-NFVixqfFEEAnrlGnwwmpxe1RKwmIog" http://saturn.picoctf.net:58554/base/books/pdf/5 -o flag.pdf
🚩 Flag Capture
Flag
Proof of Execution
🔧 Tools Used
Tool Purpose jwt.io JWT Forging ChromeDevTools Web Testing
💡 Key Learnings
Time Optimization
- In the case of JWT, immediately check if there is any key leak in the attached files, and then the structure to obtain admin access.
Skills Improved
- Binary Exploitation
- Reverse Engineering
- Web Exploitation
- Cryptography
- Forensics
- OSINT
- Miscellaneous
📚 References & Resources
Learning Resources
📊 Final Statistics
Metric | Value | Notes |
---|---|---|
Time to Solve | 00:35 | From start to flag |
Global Ranking (At the time of flag submission) | Challenge ranking | |
Points Earned | Team contribution |
Created: 11-02-2025 • Last Modified: 11-02-2025 *Author: mH4ck3r0n3 • Team: *