api.mentorquotes.htb


A virtual host / sub-domain has been discovered through fuzzing; api.mentorquotes.htb

┌──(kali㉿kali)-[~/archive/htb/labs/mentor]
└─$ curl http://api.mentorquotes.htb/ -I
HTTP/1.1 404 Not Found
date: Wed, 27 Dec 2023 11:21:54 GMT
server: uvicorn
content-length: 22
content-type: application/json

Webroot It appears to be an API server powered by uvicorn as it returns some JSON data despite of that “404”

uvicorn is a lightweight and fast ASGI (Asynchronous Server Gateway Interface) server implementation for Python web applications. It is specifically designed to work seamlessly with asynchronous frameworks such as FastAPI. Uvicorn provides high performance, supporting HTTP1.1, and WebSocket protocols, making it suitable for modern web development. Its simplicity, speed, and compatibility with asynchronous programming make it a popular choice for deploying and serving asynchronous Python web applications.

A strength of the ASGI protocol is that it decouples the server implementation from the application framework. This allows for an ecosystem of interoperating webservers and application frameworks.

Fuzzing


┌──(kali㉿kali)-[~/archive/htb/labs/mentor]
└─$ ffuf -c -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt -t 200 -u http://api.mentorquotes.htb/FUZZ -ic
________________________________________________
 :: Method           : GET
 :: URL              : http://api.mentorquotes.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 200
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
docs                    [Status: 200, Size: 969, Words: 194, Lines: 31, Duration: 68ms]
users                   [Status: 307, Size: 0, Words: 1, Lines: 1, Duration: 143ms]
admin                   [Status: 307, Size: 0, Words: 1, Lines: 1, Duration: 113ms]
quotes                  [Status: 307, Size: 0, Words: 1, Lines: 1, Duration: 54ms]
redoc                   [Status: 200, Size: 772, Words: 149, Lines: 28, Duration: 55ms]
server-status           [Status: 403, Size: 285, Words: 20, Lines: 10, Duration: 75ms]
:: Progress: [207630/207630] :: Job [1/1] :: 962 req/sec :: Duration: [0:03:28] :: Errors: 0 ::

ffuf returned a few endpoints and 2 of them appears to be interactive pages; docs and redoc

docs


The /docs endpoint showcases a list of available API endpoints with examples

  • This confirms the theory that api.mentorquotes.htb is indeed hosting an API server
  • The page does also reveal a username, james

james user


The personal blog with quotes hosted on the mentorquotes.htb host appears to be fetching the quotes from here The james user is also implied to be the website’s owner. Thus, the user could be an admin user

/Quotes/ API Endpoint


It would appears that the /quotes/ API endpoint requires the Authorization header

┌──(kali㉿kali)-[~/archive/htb/labs/mentor]
└─$ curl -X 'GET' \
  'http://api.mentorquotes.htb/quotes/' \
  -h 'accept: application/json'
{"detail":[{"loc":["header","Authorization"],"msg":"field required","type":"value_error.missing"}]}                                                                                                                                        

It wouldn’t work otherwise.

It seems to be responded by the HTTPValidationError schema

/auth/signup API Endpoint


There is also an API endpoint for signing up; /auth/signup Unlike the /quotes/ API endpoint, the auth/signup API endpoint only requires a request body with some POST data

I will test it out and generate one

┌──(kali㉿kali)-[~/archive/htb/labs/mentor]
└─$ curl -X 'POST' \
  'http://api.mentorquotes.htb/auth/signup' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "email": "qweqwe@example.net",
  "username": "qweqwe",
  "password": "blahblah123"
}'
{"id":8,"email":"qweqwe@example.net","username":"qweqwe"}

Looking at the server response, signup was a success and the testing account, qweqwe:blahblah123, has been given "id":8 I will now use this newly created testing account to authenticate

/auth/login API Endpoint


As the name suggests, the /auth/login API endpoint support authentication. My guess is that the server will response with an authorization string as a session cookie

Generating the curl command

┌──(kali㉿kali)-[~/archive/htb/labs/mentor]
└─$ curl -X 'POST' \
  'http://api.mentorquotes.htb/auth/login' \
  -h 'accept: application/json' \
  -h 'content-type: application/json' \
  -d '{
  "email": "qweqwe@example.net",
  "username": "qweqwe",
  "password": "blahblah123"
}'
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InF3ZXF3ZSIsImVtYWlsIjoicXdlcXdlQGV4YW1wbGUubmV0In0.uN4u-axWfgb9Ujrm0hA88y8Y4CkRmQFeteQLIDKKQm0"

the authentication seems successful and i have been given what appears to be a jwt Now, with the authorization string, I should be able to fetch quotes from the /quotes/ API endpoint

/quotes/ API Endpoint with authorization header


Generating the request command

┌──(kali㉿kali)-[~/archive/htb/labs/mentor]
└─$ curl -X 'GET' \
  'http://api.mentorquotes.htb/quotes/' \
  -H 'accept: application/json' \
  -H 'Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InF3ZXF3ZSIsImVtYWlsIjoicXdlcXdlQGV4YW1wbGUubmV0In0.uN4u-axWfgb9Ujrm0hA88y8Y4CkRmQFeteQLIDKKQm0'
[{"title":" I believed I was good","description":"I was so bad at studies in school. Teachers used to tell me that I should follow a different passion other than typical education. Nonetheless, I got rid of the negativity in myself and others and worked as hard as I could in my finals and college education. Now I am a paid accountant for a major brand in my country.","id":1},{"title":"Find your passion before its too late","description":"When I was growing up, I did not really have much, sure I enjoyed my passion but did not take it seriously. When I worked in a gas station for 3 years at that point I made a decision to go back to education and get a masters degree. Now I am a senior content engineer for a reputed company","id":2},{"title":"Never too late","description":"I was a construction worker for almost 10 years. I had to work there because I did not have any educational background to work in the academic sector. But I realized I was not getting the best of my life. So I started investing in crypto. Learned about how investing is done professionally. Soon enough I was able to give up the construction sector and start my own business. I did all this in my 60s","id":3},{"title":"Age is just a number","description":"When I was was kid, I used to create very simple video games as a hobby. Because I loved it so much, I thought to myself that I am going to be a game developer someday. With self-motivation, I applied for a job as a game designer when I just finished school. Now I work there as a permanent video game developer. And I am really proud to be the youngest employee there.","id":4},{"title":"Its all worth it in the end","description":"Working as a school teacher, I had a lot of responsibilities to fulfill. Even though I really loved teaching kids, the pressure was unbearable. I had 10-hour shifts at school including all the extra classes. But I did all of them to support the kids who needed my help. Now I am retired and I can not think of anything else that I would have done except this. When I see the kids I teach come and visit me telling how they enjoyed my teaching, all the hard work I put in is worth it.","id":5}]                                                                                                                                        

It worked. Those are the same 5 quotes, seen in the mentorquotes.htb host

/quotes/ API Endpoint for Create and Delete


┌──(kali㉿kali)-[~/archive/htb/labs/mentor]
└─$ curl -X 'POST' \
  'http://api.mentorquotes.htb/quotes/' \
  -h 'accept: application/json' \
  -h 'content-type: application/json' \
  -h 'authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InF3ZXF3ZSIsImVtYWlsIjoicXdlcXdlQGV4YW1wbGUubmV0In0.uN4u-axWfgb9Ujrm0hA88y8Y4CkRmQFeteQLIDKKQm0' \
  -d '{
  "title": "test",
  "description": "This is a testing quote"
}'
{"detail":"Only admin users can access this resource"}

While creating a quote takes 2 parameters, title and description, it does require admin privileges The current testing account does not have admin privileges

┌──(kali㉿kali)-[~/archive/htb/labs/mentor]
└─$ curl -X 'DELETE' \
  'http://api.mentorquotes.htb/quotes/1/' \
  -h 'authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InF3ZXF3ZSIsImVtYWlsIjoicXdlcXdlQGV4YW1wbGUubmV0In0.uN4u-axWfgb9Ujrm0hA88y8Y4CkRmQFeteQLIDKKQm0' \
  -h 'accept: application/json'
{"detail":"Only admin users can access this resource"}                                                                                                                                        

Same goes for deletion

redoc


The /redoc page is the documentation page for the API server