Tracker
- User
- root
Loot
Proofs
File | Flag |
---|---|
user.txt | e1156acc3574e04b06908ecf76be91b1 |
root.txt | 1722e99ca5f353b362556a62bd5e6be0 |
Passwords
Username | Hash | Cleartext | Notes |
---|---|---|---|
myP14ceAdm1nAcc0uNT | manchester | mypage login | |
tom | spongebob | mypage login | |
mark | snowflake | mypage login | |
rastating | mypage login | ||
mark | 5AYRft73VtFpc84k | ssh login |
Interesting Artifacts
Artifact | Original Path | Saved Path | Notes |
---|---|---|---|
myplace.zip | pwd: magicword | ||
db.tasks.insert( { "cmd" : "cp /bin/dash /tmp/test; chown tom:admin /tmp/test; chmod 6755 /tmp/test;" } ) | CE in context of tom |
Summary
OS: Linux 4.4.0-93-generic
Distribution: Ubuntu 16.04.3 LTS
Architecture: x86_64
FQDN: ?
vhosts: ?
Lessons Learned
I can abuse the suid bit on the dash shell binary, but I will most likely have to pass the '-p' flag, or dash will squash it and spawn with my regular context uid/gid.
Sometimes you just have to brute force ASLR. I don't know why ippsecs libc base addr worked and mine didn't, that was some bullshit. Weird.
Solution
Enumeration
Open Ports
ssh on tcp/22
hadoop-tasktracker on tcp/3000
Manual Enumeration
I began by enumerating this machine with three Nmap scans, a quick tcp scan, a full port tcp scan, and a top 20 udp port scan. I followed these scans up with service specific Nmap script scans on all exposed ports I discovered.
SSH supports pubkey and pwd login.q
Ok, so based on the full nmap scan results, it appears that Apache Hadoop is a web server still.
Now that I know that, I ran an Nmap http script scan against tcp/3000.
❯ nmap -vv \--reason -Pn -sV -p 3000 \--script=\"banner,http\*\" -oN \"/home/borari/ctf/htb/boxes/10.10.10.58-node/scans/tcp_3000_http_nmap.txt\" 10.10.10.58
While that scan is running, I decided to navigate to the port directly from my web browser, proxied through Burp so I can generate a sitemap.
Ok, gobuster/wfuzz isn't going to work here. There is a wildcard redirect, so all my requests get redirected to index.html, so the fuzzer sees return codes of 200 for everything.
Instead of manually fuzzing the web directories, I can manually enumerate them with the main javascript file, app.js. I did test changing the --hc flag to '--hc 200', and it did work. All the 200 requests were filtered out, and folders like assets were returned because they were 301s. In theory, this would work to show me files I don't have permissions for and stuff.
The returned contents of app.js shows that there is an admin page.
Navigating to it, it seems like I should be able to inject a username somehow, since it is referring to me as {{user.username}}. Hitting the download backup button doesn't do anything.
partials/home.html showed a weird template type of home page.
The partials/login.html page shows a raw login page. This will most likely be brute-forceable. The regular login page did not appear to be, because it passed the credentials with an API.
Oh, so poking around in the sitemap tree revealed the api/users/ folder. Looking at the / folder, I saw that it was a list of usernames and what appeared to be hashed passwords.
It looks like these hashes are SHA-256.
I add them to a .hashes file, then run hashcat against the file.
hashcat -m 1400 -a 0 \--username loot/m1400.sha256.hashes /usr/share/wordlists/rockyou.txt
Hashcat cracks 3 of the 4 password hshes, including the hash for the admin user.
Logging in to /partial/login.html doesn't work, but logging in the /login does. I am redirected to a welcome page with another button to download backup.
The file appears to be obsfucated, but it doesn't look like It's actually encrypted or anyting.
Can I base64 decrypt it? Uh, apparently it might be a binary file?
Using binwalk, I confirm that this is actually a binary zip file.
Unzipping requires a password. Using manchester doesn't work. I'll have to break it with john.
John was able to crack the password extremely quickly.
Ok, so this is the sourcecode of the myplace site. First things first, are there any hardcoded credentials?
grep -Ri password | less
Didn't see anything, but there was a fair amount of references to a mongo database. I looked in the main file app.js to see if I could see where the web code interacted with the database. Wait, is this a hardcoded credential in the url variable?
I am able to straight log in over ssh with that password, it's not hashed or anything.
EoP: mark to tom
It looks like user tom is an admin on this box.
Some backup tool at /usr/local/bin/backup had the setuid bit set.
User tom has two processes currently running. /usr/bin/node is acting on /var/scheduler/app.js and /var/www/myplace/app.js.
So this uses the same credentials for user mark to connect to the mongo db. Once connected it looks like it is executing commands. But, we know from ps that mongodb is running as user tom right?
I should connect to the mongodb backend and poke around.
mongo -p -u mark scheduler
So I know that the script is looping through, and if there is something called doc, then it executes doc.cmd. So I guess I need a table named doc and a line named cmd? No, I need to insert a line into the table.
That copied the file as expected, and changed the suid bit, but I am still mark in the bash shell. Looking at the man page for dash, I see that passing the -p flag will stop the shell from resetting the effective uid if it doesn't match the users uid.
Oh, this location in the filesystem may be squashing suid bit. I updated the cmd payload in mongodb to copy the binary to /tmp/.
And I am now operating in the context of tom.
User Compromise
EoP: tom to root
In order to interact with the suid backup binary, I need to be in the admin group. I update the mongodb payload cmd to chown and chmod 6755 the /bin/dash file again.
Now when I execute /tmp/dash, I have a groups membership of 1002(admin).
In order to look at the assembly of this binary, I run r2, then aaa to analyze, then print out the function list with afl. Based on this, if there aren't 3 arguments, the program will jump straight to an exit. 'vvv' will go to full graphic mode, 'VV' will enter visual graphs mode.
Now I can see that there is a message "Could not open file".
What file? I can check with strace. The file is "/etc/myplace/keys".
Ok, so I can see the contents of this file on the target machine.
I'll copy these keys into the same path on my local machine.
Buffer Overflow
Ok, first I'll check to see if there are any compile/runtime protections built into this binary. I do this with the checksec module of gdb. We see that just DEP is enabled, ASLR is not on this binary.
I set a breakpoint for main with 'b main', then ran it with 'r'.
Then searched for jump call locations with 'jmpcall'. I was looking to see if there were any jmp esp's, but I was not lucky.
Since there are no jmp esp's I can take advantage of, the next easiest attack vector against a binary like this would be a "return to libc attack" with brute forcing, since we have unlimited tries/access to this binary.
The strcpy function call in main is vulnerable, it should be a strncpy call.
The strcpy at 0x08048670 has quite a few X-Refs to it, including address sym.displayWarning, sym.displaySuccess, and sym.DisplayTarget.
I didn't recognize any of the push str values in displayWarning or displaySuccess, but I do recognize some in displayTarget, particularly the "Starting archiving" string.
I went to the address (0x0804914e) that holds the call displayTarget command in the graph of main. There is a function just before the call as well.
Since this function is called displayTarget, I am guessing that it copies the string entered as the 3rd argument when the binary is called. To test this, I sent a buffer of 520 A's to the program as the third argument, and I confirmed that there was a segfault.
I confirmed that my buffer overflow occurs after 512 bytes. EIP overwrite occurs between bytes 513-517.
Back on the target machine, I use 'ldd' to check what the libc base address is for the backup binary, then I put that in for the libc_base_addr variable in buf.py.
Next, I use readelf to get the system offset, and enter that into buf.py.
Then I used readelf to get the exit offset and enter it into buf.py.
The argument offset is where /bin/sh is, but that changes. To find it, I used strings on libc.so.6.
I execute the buffer overlow python script. On Try 182, I get a shell!