HackTheBox - Medium - Linux - Format

2023-12-24 05:47:33

Format

Format 是一种中等难度的 Linux 机器,它突出显示了由解决方案的结构方式引起的安全问题。立足点涉及PHP源代码审查,发现和利用本地文件读/写漏洞,并利用Nginx中的错误配置在Redis Unix套接字上执行命令。横向移动包括浏览 Redis 数据库以发现用户密码,而权限提升则围绕以 root 权限运行的 Python 脚本展开,该脚本容易受到代码注入的影响。


外部信息收集

端口扫描

循例nmap

file

Web枚举

80端口 - app.microblog.htb

file

3000端口 - microblog.htb

file

Foldhold

和我预想的一样,gitea包含了app子域的源码,毕竟遇到这种情况的次数也不少了

file

我们首先在app子域创建一个账号并登录

file

这里有一个最显眼的功能也是唯一一个功能就是创建新博客,我们可以指定子域域名,我们在源码里面定位到那个功能的代码

一开始我把目光放到了addsite函数上

addSite($_POST['new-blog-name']);


function addSite($site_name) {
    if(isset($_SESSION['username'])) {
        ...
        $tmp_dir = "/tmp/" . generateRandomString(7);
        system("mkdir -m 0700 " . $tmp_dir);
        system("cp -r /var/www/microblog-template/* " . $tmp_dir);
        system("chmod 500 " . $tmp_dir);
        system("chmod +w /var/www/microblog");
        system("cp -rp " . $tmp_dir . " /var/www/microblog/" . $site_name);
		...

我们应该可以尝试控制$_POST[‘new-blog-name’]来尝试执行系统命令,但是我发现后端对new-blog-name进行了严格的过滤,只允许26个字母,所以使我打消了这个念头

if (isset($_SESSION['username']) && isset($_POST['new-blog-name'])) {
    if(!preg_match('/^[a-z]+$/', $_POST['new-blog-name']) || strlen($_POST['new-blog-name']) > 50) {
        print_r("Invalid blog name");

我创建了一个test子域

在gitea中我看到一个sunny子域,我猜那个应该是由app子域创建的,所以我刚刚创建的test子域应该与sunny子域有同样的代码结构

file

在edit/index.php中,我发现了任意文件读写

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

//add text
if (isset($_POST['txt']) && isset($_POST['id'])) {
    chdir(getcwd() . "/../content");
    $txt_nl = nl2br($_POST['txt']);
    $html = "<div class = \"blog-text\">{$txt_nl}</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");
}

尝试读取passwd

file

我尝试着在web目录下写入webshell,但很不幸的是应该是没有权限和解析php

Nginx配置错误

读/etc/nginx/sites-available/default

...
location ~ /static/(.*)/(.*) {
	resolver 127.0.0.1;
	proxy_pass http://$1.microbucket.htb/$2;
...

这篇文章讲述了nginx的proxy_pass支持将请求代理到本地unix套接字

现在我们可以控制$1,我们看到register.php设置的字段

file

通过nginx的proxy_pass来对redis进行操作

将自己的账户的pro字段设置为true

file

刷新一下

file

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

现在我们是pro,上面这些命令会使我们能够对uploads/目录写入文件,借此来getshell

file

然后通过rce来使用祖传python3 payload

file

本地横向移动 -> cooper

在redis发现了一个系统用户名的key

file

读一下发现了他的密码

hgetall cooper.dooper

file

直接登ssh

file

本地权限提升

sudo -l

file

这是一个python脚本

我们主要关注脆弱点

    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)

这里的license_key由几个变量拼接进来然后format,应该存在模板注入,而username这些都是在redis读取的,我们可以控制redis来触发模板注入

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())

通过模板注入来获取secret,设置username

file

再次运行

file

root flag还在老地方

file

文章来源:https://blog.csdn.net/qq_54704239/article/details/135176440
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。