Web
Nmap discovered a web server on the target port 80
The running service is nginx 1.18.0
Webroot
It’s a login page.
Cookie
Intercepting the request through Burp Suite, I notice that there are 3 sets of cookies
token
seems to be either JWT or Flask TokenXSRF.TOKEN
just a generic XSRF/CSRF preventionintentions_session
seems to be a custom session token
token
┌──(kali㉿kali)-[~/archive/htb/labs/intentions]
└─$ jwt_tool 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vMTAuMTAuMTEuMjIwL2FwaS92MS9hdXRoL2xvZ2luIiwiaWF0IjoxNjg4Mzg0MTE0LCJleHAiOjE2ODg0MDU3MTQsIm5iZiI6MTY4ODM4NDExNCwianRpIjoiQW9zMWJnTjNtUjcyNHNTdSIsInN1YiI6IjMyIiwicHJ2IjoiMjNiZDVjODk0OWY2MDBhZGIzOWU3MDFjNDAwODcyZGI3YTU5NzZmNyJ9.6N2yJxp3FEKUqJvKjyUNdJuYUSq6rZCYAtoYy2AcrdY'
\ \ \ \ \ \
\__ | | \ |\__ __| \__ __| |
| | \ | | | \ \ |
| \ | | | __ \ __ \ |
\ | _ | | | | | | | |
| | / \ | | | | | | | |
\ | / \ | | |\ |\ | |
\______/ \__/ \__| \__| \__| \______/ \______/ \__|
Version 2.2.6 \______| @ticarpi
original jwt:
=====================
decoded token values:
=====================
token header values:
[+] typ = "JWT"
[+] alg = "HS256"
token payload values:
[+] iss = "http://10.10.11.220/api/v1/auth/login"
[+] iat = 1688384114 ==> timestamp = 2023-07-03 13:35:14 (UTC)
[+] exp = 1688405714 ==> timestamp = 2023-07-03 19:35:14 (UTC)
[+] nbf = 1688384114 ==> timestamp = 2023-07-03 13:35:14 (UTC)
[+] jti = "Aos1bgN3mR724sSu"
[+] sub = "32"
[+] prv = "23bd5c8949f600adb39e701c400872db7a5976f7"
seen timestamps:
[*] iat was seen
[*] exp is later than iat by: 0 days, 6 hours, 0 mins
----------------------
jwt common timestamps:
iat = IssuedAt
exp = Expires
nbf = NotBefore
----------------------
This one is indeed a JWT token
It seems that the authentication is made to the API endpoint at
/api/v1/auth/login
intentions_session
┌──(kali㉿kali)-[~/archive/htb/labs/intentions]
└─$ echo 'eyJpdiI6ImZacDhOcVlDMWxNcGNMcXYrc0htaXc9PSIsInZhbHVlIjoiYTdOQytGck5iVUZFamkxR1dKMlIzWlc4cDc3SldrZGFYRmdudDBDYUZmY3J0UG1JWXJCcFRGcG0zcGcwMlBPTmkwKzl5cnBFMDN1eGpLdzZIL01WVGE1WnkzZXl4SUw0TjdhZjczUWxPdkgrL1g2ZHVLcWNpR0dNNjFwU2NLWnEiLCJtYWMiOiI1YTM3ODJkNDlkMDljZjhiNTI4ZDk0ZmE3MjdiODY4Mzk5YjUzNWM5NWRiMzMxMmUyNWNiMDcyYzY3ZDc1ZGJkIiwidGFnIjoiIn0%3D' | base64 -d
{"iv":"fZp8NqYC1lMpcLqv+sHmiw==","value":"a7NC+FrNbUFEji1GWJ2R3ZW8p77JWkdaXFgnt0CaFfcrtPmIYrBpTFpm3pg02PONi0+9yrpE03uxjKw6H/MVTa5Zy3eyxIL4N7af73QlOvH+/X6duKqciGGM61pScKZq","mac":"5a3782d49d09cf8b528d94fa727b868399b535c95db3312e25cb072c67d75dbd","tag":""}base64: invalid input
Although incomplete, the string appears to be encoded in the base64 format as it shows some relevant JSON data
Register / Login
Creating a testing account and logging in
The menu bar on the left-hand side consists of a few items
The Gallery button appears to display all the images
Individual images have a tag assigned.
There are a total of 4 as far as those those displayed images show
Those images are located in the sub-directories under the
/storage
directory
Names are very much arbitrary yet structural as if those were generated
I seem to be able to set the *Favorite Genres
Which is then reflected on My feed
Fuzzing
┌──(kali㉿kali)-[~/archive/htb/labs/intentions]
└─$ ffuf -c -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://$IP/FUZZ -ic -e .txt,.php,.html
________________________________________________
:: Method : GET
:: URL : http://10.10.11.220/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
:: Extensions : .txt .php .html
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
index.php [Status: 200, Size: 1523, Words: 415, Lines: 40, Duration: 128ms]
gallery [Status: 302, Size: 322, Words: 60, Lines: 12, Duration: 106ms]
admin [Status: 302, Size: 322, Words: 60, Lines: 12, Duration: 113ms]
storage [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 192ms]
css [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 204ms]
js [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 243ms]
logout [Status: 302, Size: 322, Words: 60, Lines: 12, Duration: 294ms]
robots.txt [Status: 200, Size: 24, Words: 2, Lines: 3, Duration: 205ms]
fonts [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 209ms]
ffuf made a few discoveries.
- The
index.php
file is the web root - The
robots.txt
file seems to be rather empty /admin
and/storage
returns the code 301, likely locked behind the authentication
- There is the
/js
directory
JS
Checking the Burp Crawler reveals that the web server keeps loading up 3 JS files
While those JS files appear to be heavily obfuscated for security, little bit of reading suggests that the target web application is running off of these scripts
There are many API endpoints
genres
One of the API endpoints reveals the current sessions user data in the JSON format
This is the testing account that I created earlier.
As shown above, I am able to modify the values of the
genres
parameter
The
genres
parameter seems to be affecting what gets displayed on to /gallery#/feed
Additionally,
/gallery#/feed
pulls resources from the API endpoint at /api/v1/gallery/user/feed
This suggests a potential for the 2nd order attack
Fuzzing /js/
┌──(kali㉿kali)-[~/archive/htb/labs/intentions]
└─$ ffuf -c -w /usr/share/wordlists/seclists/discovery/web-content/directory-list-2.3-medium.txt -u http://$IP/js/FUZZ -ic -e .js
________________________________________________
:: Method : GET
:: URL : http://10.10.11.220/js/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
:: Extensions : .js
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
login.js [status: 200, Size: 279176, Words: 5446, Lines: 2, Duration: 92ms]
gallery.js [status: 200, Size: 310841, Words: 6285, Lines: 2, Duration: 92ms]
admin.js [status: 200, Size: 311246, Words: 6584, Lines: 2, Duration: 91ms]
app.js [status: 200, Size: 433792, Words: 7736, Lines: 2, Duration: 236ms]
mdb.js [status: 200, Size: 153684, Words: 2246, Lines: 2, Duration: 298ms]
ffuf discovered admin.js
and app.js
in the /js
directory
admin.js
There are some parts within the script with mentions of ReactNative and NativeScript
Here is the most interesting part.
It seems that someone left a note in the admin GUI panel
Note
Recently we've had some copyrighted images slip through onto the gallery. This could turn into a big issue for us so we are putting a new process in place that all new images must go through our legal council for approval. Any new images you would like to add to the gallery should be provided to legal with all relevant copyright information. I've assigned Greg to setup a process for legal to transfer approved images directly to the server to avoid any confusion or mishaps. This will be the only way to add images to our gallery going forward.
Hey team, I've deployed the v2 API to production and have started using it in the admin section.
Let me know if you spot any bugs. This will be a major security upgrade for our users, passwords no longer need to be transmitted to the server in clear text! By hashing the password client side there is no risk to our users as BCrypt is basically uncrackable.
This should take care of the concerns raised by our users regarding our lack of HTTPS connection.
the v2 api also comes with some neat features we are testing that could allow users to apply cool effects to the images. i've included some examples on the image editing page, but feel free to browse all of the available effects for the module and suggest some:
The note above mentions the following;
Greg
might be an admin- The second version of the API being already deployed to the admin section of the web app.
- Passwords no longer need to be transmitted in the cleartext as the v2 API requires hashing on the client side with BCrypt
- The v2 API also allows users to apply effects to the images
v2 API Authentication
Attempting to authenticate to the v2 API, indeed fails with a message that the hash field is required