Here we go again, this time we are looking at Luke. What do we know before we begin? Very little, apart from it’s a FreeBSD box on the IP 10.10.10.137.
So let’s start the same way as always!
nmap -sC -sV -O -oA nmap/Luke 10.10.10.137
We get a few ports that show as open:
Nmap 7.70 scan initiated Sun Jun 23 14:22:36 2019 as: nmap -sC -sV -O -oA nmap/luke 10.10.10.137 Nmap scan report for 10.10.10.137 Host is up (0.087s latency). Not shown: 995 closed ports PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3+ (ext.1) | ftp-anon: Anonymous FTP login allowed (FTP code 230) |_drwxr-xr-x 2 0 0 512 Apr 14 12:35 webapp | ftp-syst: | STAT: | FTP server status: | Connected to 10.10.14.33 | Logged in as ftp | TYPE: ASCII | No session upload bandwidth limit | No session download bandwidth limit | Session timeout in seconds is 300 | Control connection is plain text | Data connections will be plain text | At session startup, client count was 1 | vsFTPd 3.0.3+ (ext.1) - secure, fast, stable |_End of status 22/tcp open ssh? 80/tcp open http Apache httpd 2.4.38 ((FreeBSD) PHP/7.3.3) | http-methods: |_ Potentially risky methods: TRACE |_http-server-header: Apache/2.4.38 (FreeBSD) PHP/7.3.3 |_http-title: Luke 3000/tcp open http Node.js Express framework |_http-title: Site doesn't have a title (application/json; charset=utf-8). 8000/tcp open http Ajenti http control panel |_http-title: Ajenti No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ). TCP/IP fingerprint: OS:SCAN(V=7.70%E=4%D=6/23%OT=21%CT=1%CU=37528%PV=Y%DS=2%DC=I%G=Y%TM=5D0F7DD OS:F%P=x86_64-pc-linux-gnu)SEQ(SP=105%GCD=1%ISR=109%TI=Z%CI=Z%II=RI%TS=22)O OS:PS(O1=M54DNW6ST11%O2=M54DNW6ST11%O3=M54DNW6NNT11%O4=M54DNW6ST11%O5=M54DN OS:W6ST11%O6=M54DST11)WIN(W1=FFFF%W2=FFFF%W3=FFFF%W4=FFFF%W5=FFFF%W6=FFFF)E OS:CN(R=Y%DF=Y%T=40%W=FFFF%O=M54DNW6SLL%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F OS:=AS%RD=0%Q=)T2(R=N)T3(R=Y%DF=Y%T=40%W=FFFF%S=O%A=S+%F=AS%O=M54DNW6ST11%R OS:D=0%Q=)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0% OS:S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7( OS:R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0 OS:%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=S%T=40%CD=S) Network Distance: 2 hops OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Sun Jun 23 14:25:51 2019 -- 1 IP address (1 host up) scanned in 195.57 seconds
So we have 5 ports open, 3 of which are webservers and one which allows Anonymous FTP!
I know where I’m going to start! Let’s start by setting 3 dirbs running in the background while we investigate the FTP server.
dirb http://10.10.10.137:80 -w /usr/share/dirb/wordlists/common.txt > dirb/port80.txt dirb http://10.10.10.137:3000 -w /usr/share/dirb/wordlists/common.txt > dirb/port3000.txt dirb http://10.10.10.137:8000 -w /usr/share/dirb/wordlists/common.txt > dirb/port8000.txt
While they are running, let’s take a look at the FTP server. Nmap has already told us we can log in anonymously, so let’s give it a go
ftp 10.10.10.137 21
We get prompted for a password, entering anything gives us access.
We can see there is 1 folder called webapp. Within there is a file called for_Chihiro.txt
Let’s get that back to our box
get for_Chihiro.txt
We now have this on our box. Cat’ing the file shows it contains a message:
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
Now, what the fuck does that mean?! I’m guessing maybe we need to look at the source of each of the websites on each of the 3 webservers!
Shall we start with the old fashioned port 80.
Heading over to the website we get a very basic website, with some text and 3 links which take you to further down the page. From looking at the source we do know that there is a lot of javascript on the website.
Unfortunately I know absolutely nothing about javascript or how to exploit it. Let’s see if our dirb came back with any results for this webserver.
It came back with a few pages and directories
/css/ /index.html /js/ /LICENSE /management /member/ /vendor/
Management, LICENSE and index look quite interesting, lets have a look.
First up, management. We get a pop up!
If we can find some creds, I know where I’m coming back to.
LICENSE shows the MIT License, I think this is for the bootstrap JS stuff we saw earlier.
Index is the main page that we see, which I don’t think has much on.
In terms of folders, member is empty, css is just the display schemes, vendor has a list of all the javascript used:
bootstrap jquery-easing jquery
It looks like the jQuery is v3.3.1, jQuery Easing is v1.4.1 and Bootstrap is v4.2.1.
We may come back to these for exploits or at least more googling and the version numbers are bound to be useful.
The final folder, is /js/ This just looks to contain the scrolling-nav, which the hotlinks use to scroll down the page.
As I have no idea what we could even do here, let’s go look at the other ports.
Port 3000 appears to just have some JSON on it
This looks a lot like what we found on Port 3000. We quickly ran away then but maybe another viewing of ippsecs video will give us a hint.
However, the dirb that I ran did find 3 directories:
/login /Login /users
Let’s go check them out.
Both Login and login give a JSON message, it’s the same on both and simply says:
"please auth"
Users has a slightly different answer:
success false message "Auth token is not supplied"
It’s not much better!
Let’s skip over these for now and head over to port 8000. Aha, slightly more interesting:
It’s a login prompt. So we have 3 areas that require auth:
http://10.10.10.137/management http://10.10.10.137:3000/login http://10.10.10.137:8000
Now, just to find some creds I guess!
The directory wordlist that I used was a different one to normal, so I’m going to try using dirbuster which isn’t my favourite to look at but gives good results with my normal wordlist:
/usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt
Let’s see if there is anything extra we get.
We get an additional hit on port 80 which I’m not sure how we missed first time around:
/config.php /login.php This gives us some db information:
$dbHost = 'localhost'; $dbUsername = 'root'; $dbPassword = 'Zk6heYCyv6ZE9Xcg'; $db = "login"; $conn = new mysqli($dbHost, $dbUsername, $dbPassword,$db) or die("Connect failed: %s\n". $conn -> error);
So we have a username and password, potentially. Let’s go see if it works on the logins!
So trying that password with:
root Chihiro Derry
Unfortunately none of these worked with that password.
I have also gone back to basics and looked at the default password for Ajenti which is:
root:admin
This also doesn’t work, but would indicate it could be where the root password goes. Looking at searchsploit for Ajenti we have 1 hit:
It’s cross-site scripting. Not too sure how useful that will be here.
I think maybe we need to go back to the javascript on the main webserver and have a look at what we have.
The one thing that keeps bugging me is port 3000 and the message please auth or auth token is not supplied for users.
Maybe we can use the password as an auth token? Event getting a list of users at this point would be good.
Thinking about the list of users, there is the directory /users/ we tried that earlier and got a please auth message. What if we try some user names after that so /users/chihiro
We get:
Whereas if we try derry we get:
What this means, is we get a different result for a real user, therefore we can enumerate usernames.
So let’s put that request through burp and send it over to intruder. Dirb has a good name list which is available at /usr/share/wordlists/dirb/others/names.txt
So in Intruder, we set the position around derry
We load in the wordlist under Payload (Simple List) and add in a few others including derry, luke and admin. Then within the options we add under Grep Match
Hit start attack and let’s watch the results come pouring in! After we hit start attack we filter out any 404 errors!
I have just realised that doing a match isn’t the best, as what if there is a website that loads straight away, we won’t see if as it doesn’t match our expected outcome.
So re-running the test without that grep. Anything that doesn’t load will still come back as a 404 so we can filter those out.
While that was running, a colleague had mentioned trying curl (not sure if he knows something I don’t, has read something or is just also clutching at straws), so I looked at some curl commands to see if we could get any more clues.
curl http://10.10.10.137 {"success":false,"message":"Auth token is not supplied"}
Hmm ok, how about /login
curl http://10.10.10.137:3000/login "please auth"
So, let’s try and add authentication, we have a username and password
curl --user root:Zk6heYCyv6ZE9Xcg http://10.10.10.137:3000/login
Another “please auth”, so wrong user or password or syntax/format. Let’s try some other users.
curl --user admin:Zk6heYCyv6ZE9Xcg http://10.10.10.137:3000/login curl --user Admin:Zk6heYCyv6ZE9Xcg http://10.10.10.137:3000/login curl --user superadmin:Zk6heYCyv6ZE9Xcg http://10.10.10.137:3000/login curl --user SuperAdmin:Zk6heYCyv6ZE9Xcg http://10.10.10.137:3000/login curl --user administrator:Zk6heYCyv6ZE9Xcg http://10.10.10.137:3000/login curl --user Administrator:Zk6heYCyv6ZE9Xcg http://10.10.10.137:3000/login curl --user derry:Zk6heYCyv6ZE9Xcg http://10.10.10.137:3000/login curl --user Derry:Zk6heYCyv6ZE9Xcg http://10.10.10.137:3000/login
All the exact same result:
I guess we wait for the Burp Intruder to finish and see if we have any other usernames we can try out.
After what can only be described as …..ages….. I remembered that intruder is very slow on burp community edition. However, we are enumerating directories, why don’t we go back to dirbuster for this!
So, running dirbuster, much quicker we get a list of users which exist:
That runs super quickly and we have another couple of usernames:
/Admin/
/Dory/
/Yuri/
So, we can now try logging into each auth prompt with these 2 additional usernames. None of the 3 direct auth prompts work for either username with the password found from the config.php file.
Heading to /users/<name> still gives the “Auth token is not supplied”
This is, frustrating to say the least!
Doing some research and finding some pretty diagrams to explain JWTs it look slike we need to authenticate to /login with a username and password, then the server will create a JWT which we can then use the get to all the other subdirectories.
So the big question is how do we authenticate using /login which doesn’t have any sort of GUI. We will need to do this by putting the requests directly in a POST request, however we don’t know the field names for username and password, we don’t know which user to use or even if the password is correct.
That’s far too many variables for my liking!
With this new knowledge, I did some more googling on authenticating to express JS, I revisitied the idea of CURL that we did earlier and read through this article: https://medium.com/@evangow/server-authentication-basics-express-sessions-passport-and-curl-359b7456003d
This showed:
client $ curl -X POST http://localhost:3000/login -c cookie-file.txt -H 'Content-Type: application/json' -d '{"email":"test@test.com", "password":"password"}'You were authenticated & logged in!
So, I knew that we didn’t have an e-mail, but maybe it was username, so I tried that. I figured at this stage, we needed this to be admin but I was ready to do a lot of manual trying through all the usernames if that didn’t work.
My final request was:
curl -X POST http://10.10.10.137:3000/login -c cookie-file.txt -H 'Content-Type: application/json' -d '{"username":"admin", "password":"Zk6heYCyv6ZE9Xcg"}'
This resulted in:
{"success":true,"message":"Authentication successful!","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTYxODg2NDQzLCJleHAiOjE1NjE5NzI4NDN9.-I_yLS8xOvod3dCyGeKqdmUrClsfXW7ESf_GaZF540E"}
We now have a token!
Next step, how to use that!
Firstly, I was interested to see what the token was made up of, so I went over to calebb.net which decodes JWT tokens, the results showed:
{ alg: "HS256", typ: "JWT" }. { username: "admin", iat: 1561886443, exp: 1561972843 }. [signature] |
So the username is admin, it has a code and an expiry date (I assume), it doesn’t really help us but it’s interesting to know what’s there.
Looking at a JWT for beginners page here: https://jwt.io/introduction/
It mentions:
“Whenever the user wants to access a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema. The content of the header should look like the following:
Authorization: Bearer <token>"
Therefore, we are going to include that in Burp and see what we get.
We forward the request and get:
We get Dory’s username and password!
So, going through all the users we have found, we get a list of usernames and passwords.
Now that we have these, let’s head over to our login pages and see which works where.
First up, port 8000 for ajenti log in.
Unfortunately all 4 sets of creds still give us auth failure.
Let’s head over to port 80 /management and /login and see what we have,
Within management, the credentials for Derry worked and we get:
First up is config.json which has 1 very interesting line in it:
ajenti.plugins.munin.client.MuninClient "{\"username\": \"username\", \"prefix\": \"http://localhost:8080/munin\", \"password\": \"123\"}"
So we will check that out.
Config.php is the document we found earlier with the mysql creds.
Login.php takes us to the login page that we found earlier.
So let’s go take a look at port 8080/munin. Unexpectedly that doesn’t exist, if it did our port scan earlier would have caught it. Nothing works on the main login page either.
So somewhere, we have missed something.
Let’s look at our progress here. We found config.php on port 80, which gave us the creds to get the JWT token on port 3000. Then from there, we got the creds for Derry that let us get into management on port 80. From there it appears to be a deadend. I think we need to re-look at that, we must have missed something.
The full config.json might be where to go back to:
{ "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 }
So what I missed before, was the other password of KpMasng6S5EtTy9Z. This doesn’t appear to have a username attached with it, but maybe we can try some obvious ones, like root, admin etc.
Root did the trick! We are into the interface!
You may notice what I did straight away. There is a tool called “Terminal” let’s try that first!
It works! We have a terminal on the box, a very quick look around and boom, we have the user flag!
Amazing, we got the user flag!
Also what you might notice, is that cheeky whoami told me that we are root. It can’t be that easy can it?
Oh. It was that easy.
Well, that was Luke! I feel we learnt a lot about JWT creation. How to authenticate with it and bit of a hunt for creds and reminding ourselves that good enumeration is key!
There could definitely have been some priv esc, but I wonder if it’s because it’s FreeBSD and people aren’t too familiar with it? Anyway, a fun box and learnt a heap! Good times!