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