危墙——由 Rust 语言编写的新一代 Windows SSH 蜜罐
这篇文章包含章节为:
- 简介
- 更新日志:项目内容与亮点
- 一.构件
- 二.获取
- 三. 并驾齐驱,双管齐下
- 四. 十目所视,洞若观火
- 五. Rusty_Borders 终端
- 后记
- 杂项文件列表
简介
本项目为原 SSH_Honeypot For Windows 的 Rust 衍生版。
自 2024 年 03 月项目确立,作者使用 Rust 语言对 SSH-Honeypot 的 Windows 分支逐步进行了重构,后经广泛测试,现已可投入生产。
本项目遵循GPL v2.0协议。
本项目被设计为一个 Windows 环境下的 SSH 弱口令蜜罐项目,基于 Windows-OpenSSH,无前置项目。
本项目配有全面部署程序,用于自动化部署。
本项目可以使用多种方式动态监测并控制 Rusty_Borders 终端环境。
本项目使用 C++(仅全面部署程序) 和 Rust 语言开发。
作者 : Jessarin000 ,本名 Kjx52 ,现名 Skiner。
有任何意见或建议请联系作者:< kjx52@outlook.com >
本项目为开源特种类项目,作者尽力确保其普遍的适用性,但用户最好应根据开源源代码针对特定情形加以修改。
同样,作者尽力确保其安全性,但因水平有限,若是百密之中有所纰漏,还望各位告知本人。
项目链接: Rusty_Borders 危墙
(下文中 RyB 特指 Rusty_Borders 终端程序,具体原因参见第五节)
(下文中 SHW 特指 SSH_Honeypot For Windows 项目)
*是次版本相对于原项目进行了大幅更新,现作为单独项目发布* |
---|
危墙 项目标志性徽标 |
更新日志:项目内容与亮点
- 在策略上,本项目继承了 SHW 的思路:
- 以第三方伪终端替换原 SSHD 终端 CMD ,用于模拟 Linux 下的 Chroot 隔离环境,从而限制攻击者的路径变迁以及代码执行。
- 在实现上,本项目
- 跳开了其祖先所使用的内置 OS_Shell 执行用户命令,取消了守护进程,转而完全依靠 Windows API 实现,极大地提升了性能和项目安全性。
- 使用了在线控制系统和本地控制系统双重保险:
- 在线控制系统 RyB_Control_Web 程序实现了远程动态监测和控制 ALCM 列表,
可以在检查 RyB 输入、输出的同时随时更改 RyB 内可用的命令。
* 注:考虑到网站开放端口的隐患,RyB 关键命令(如 su)均由许可证控制。 - 总体调用系统 RyB_Control 程序在本地实现了使用多种命令操控 RyB 终端以及 SSH 服务。
- 在线控制系统 RyB_Control_Web 程序实现了远程动态监测和控制 ALCM 列表,
- 内置了邮件发送功能,可以在异常时提醒使用者。
- 模拟了 Linux 终端样式和 13 个常用的 Shell 命令。
- 使用 Config 配置了邮件发送参数,其余关键参数(如命令列表)均使用硬编码写入程序。
- Rust 语言本身的高性能和内存安全性以及改良后的算法使得本项目轻便而高效。
- 添加了“模块重要资源列表”,现在可以方便地查找模块内容了。
- 大量微小细节更新以及函数性能优化。
- 修复了目前已知的所有漏洞。
一.构件
它是怎么实现的?
" One Vision, One Purpose. "
本项目沿用 SHW 的设计思路:
(以下节选自SSH-Honeypot For Windows)
考虑到 Windows-OpenSSH 拥有以下限制:
- Windows-OpenSSH 配置文件中的 Chroot 选项仅在 sftp 会话中受支持,到 cmd.exe 的远程会话不会遵循 ChrootDirectory 。
- 可以通过将 shell 可执行文件的完整路径添加到 Windows 注册表 HKEY_LOCAL_MACHINE\SOFTWARE\OpenSSH 项下的 DefaultShell 字符串值来配置默认 ssh shell 。
本程序提出自定义一个可以满足要求的第三方伪终端 Bacsh(Winbash)程序来代替原 SSH 默认终端 CMD ,用于模拟 Linux 下的 Chroot 隔离环境,从而限制攻击者的路径变迁以及代码执行。
同样的,本项目使用第三方终端 RyB 程序来代替原 SSHD 默认终端 CMD,从而达到预期的效果。
危墙_Setup.exe 是使用 NSIS 完成封装的安装程序,用于初步解压所需文件。运行后即可将所有部件添加至指定位置,具体位置请参见本项目nsi脚本。
RyB_Deployment_Program.exe / 全面部署程序采用其前身——SHW 的 SSH_Deployment_Program.exe 程序的大部分源码,实现过程也与之大致相同。全面部署程序可以完成后续安装步骤,它会自动地创建弱口令用户,创建和设置各文件的权限,并且初始化工具。
由于其中有修改用户变量以及读取程序进程的步骤,所以须以管理员身份运行此程序。
在运行 危墙_Setup.exe 后,它将添加至您桌面上的 Rusty_Borders 文件夹中,并且它的快捷方式 RyB_Deployment_Program.lnk 将添加至您的桌面上。
RyB_Control.exe / 总体调用系统 或称 本地控制系统是本项目配套的启动器,同时也可以调用在线控制系统、自动化整个流程并且能够一定程度上控制 RyB,请在 OS_Shell 终端环境下将其与本机 IP 连接后执行,更多信息详见第三节。
在运行 危墙_Setup.exe 后,它将添加至您桌面上的 Rusty_Borders 文件夹中。
web_01.exe / 在线控制系统是一个由 Iron_gcd 写成的小型后端,由 总体调用系统 控制,用于在监测 RyB 输入、输出的同时控制 RyB 中可用的命令,第四节将对其进行着重介绍。
监控页面刷新频率为: 1 次 / 3 秒
在运行 危墙_Setup.exe 后,它将添加至“C:\Users\Public\SSHShell\Web_Control”文件夹中。
Rusty_Borders_危墙.exe / RyB是一个小型的终端,也是隔离环境本身。它的过滤机制继承自它的祖先—— SHW 的 Bacsh 伪终端,但又有所扬弃。除此之外,它还采用了独特的许可证系统,一把指向自己的利剑。第五节我们将讨论这个话题。
在运行 危墙_Setup.exe 后,它将添加至“C:\Users\Public\SSHShell”文件夹中。
在运行 RyB_Deployment_Program.exe 后,它将被赋予完整的、合适的权限。
杂项文件包含历史类文件(如 RyB_INFO.ryb、USER_COMMAND.ryb 等)、控制类文件(如 OutPut01.alcm)等,完整列表以及它们的位置、功能详见文末列表。
在运行 危墙_Setup.exe 后,它们将添加至各自的位置。
在运行 RyB_Deployment_Program.exe 后,它们将被赋予完整的、合适的权限。
其他信息:
- 本项目于 2024 年 03 月确立。
- 本项目搭建环境为 Windows 11 x86_64 (22631) 家庭版。
- 本项目发布构建使用的 Rust 编译器版本为: 1.78.0.
- 本项目已测试平台 :
- Windows IIS 7.0
- Windows 10 x86_64;
- Windows 11 x86、x86_64;
- 本项目于 2024-05-25 完成。
二.获取
如何部署/启动它?
本项目发布了 0.3.9 版本,本文包含已发布的所有内容。
在 NIS安装程序 目录下有使用 NSIS 完成封装的安装程序 危墙_Setup.exe。
由于本项目特殊性,请在运行安装程序后以管理员的身份运行 RyB_Deployment_Program.exe。
如果需要手动配置,可以从 include exe 目录下找到所有已编译好的 exe 可执行文件。
如果需要,可以 MainCode 目录下找到所有程序源码。
注意:
- 请在默认活动编码为 936(中文-简体)的终端环境下启动本项目启动器 RyB_Control.exe,否则可能引发乱码问题,更多语言版本请联系作者。
- 本项目需修改用户变量(添加 SSH 弱口令用户)以及注册表项(SSHD 默认终端),故请以管理员身份运行 RyB_Deployment_Program.exe 程序。
- 遇到任何问题或错误请联系作者。
三. 并驾齐驱,双管齐下
RyB_Control.exe 程序简要概述
“一为生枝,一为枯干,势凌风雨,气傲烟霞。” ——郭若虚
RyB_Control.exe,总体调用系统,或称本地控制系统,
本节中简称为 RyC。
作为协调整个项目的首脑,RyC 采用了并发的方式进行任务调度,自动化防御流程。同时,它也内置了五个简短的本地命令,用于在特殊情况下进行快速反应。
具体命令为:
命令名称 | 功能 |
---|---|
help | 显示命令列表。 |
on | 启动SSHD服务。 |
off | 关闭SSHD服务,但不关闭项目。 |
Z | 正常退出蜜罐。 |
kill | 立即终止RyB终端部分并关闭SSH服务与链接。 |
*注意,上述命令仅能在 RyC 的本地命令行下使用
当然,您也可以使用远程 Shell 启动 RyC 从而突破这个限制。不过由于 SSHD 的终端被替换,我们建议用户使用其他种类的远程 Shell,例如远程桌面。
如果您在 Linux 环境下,也可以试试 rdesktop。
与 SHW 的启动器不同,RyC 目前不支持自启动。用户需要在 OS_Shell 终端环境下将其与本机 IP 连接后执行:
用法: RyB_Control <服务器 IP 地址> ...
RyC 采用 loop 和 match 的嵌套结构接收用户命令。
下面是 RyC 的接收代码:
RyC 接收部分源码分析
...
println!("[\x1b[1;30m{}\x1b[0m] 开始监听终端指令。", sh_time());
println!(" 可以使用“\x1b[1;32mhelp\x1b[0m”命令来显示当前所有可用命令。");
let _ = io::stdout().flush();
loop
{
print!("\x1b[1;34m$\x1b[0m ");
let mut tmp1 = String::new();
let _ = io::stdout().flush();
let _ = io::stdin().read_line(&mut tmp1);
match tmp1.trim()
{
"" => continue,
"help" => ... , // 处理函数
"on" => ... , // 处理函数
"off" => ... , // 处理函数
"kill" => ... , // 处理函数
"Z" => break,
_ => println!("[\x1b[33m!\x1b[0m] 警告:命令未找到。"),
}
...
}
非常简单平实的结构,没有什么好说的。
这样看来,似乎使用 loop 结构有些大材小用了。别急,它里面还包含了更重要的结构——并发代码。
让我们看看 loop 循环中的并发部分长什么样子:
RyC 并发部分源码分析
...
let current_ip: String = ... ; // 从命令行参数解析得来的服务器IP。
print!("[\x1b[1;30m{}\x1b[0m] 在线控制系统开启于 http://{}:3000 ...", ... , current_ip);
let _ = io::stdout().flush();
let mut child = Command::new(r###"C:\Users\Public\SSHShell\Web_Control\web_01.exe"###) // 在线控制系统
.arg(current_ip)
.spawn() // 开启子线程。
.expect("子线程开启失败01");
println!("完成。");
...
loop
{
... // 命令接收部分
if let Some(state) = child.try_wait().unwrap() // 调用 try_wait() 函数检查子线程。
{
if state.success()
{
println!("[\x1b[31m-\x1b[0m] 错误:Web子线程提前终结。");
}
else
{
println!("[\x1b[31m-\x1b[0m] 错误:Web子线程诧异。");
}
... //立即退出。
}
}
...
每当 RyC 处理本地输入后,loop 循环都会调用 try_wait() 函数来检查子线程状态。
在理想情况下,若 RyC 不发送终止信号,则 web_01.exe 应永不退出,始终开放 Web 服务并处理用户请求。
在上述 loop 循环中,若 try_wait() 捕获到线程返回值,无论成功与否,都代表在线控制系统非正常终止。
故 RyC 在打印结果后立即退出。
web_01.exe 会将接受命令和处理结果打印到 RyC 所在的终端下。
并发机制使得 RyC 能够同时处理多种请求,大大提高了项目灵活性。
RyC 的启动流程如下:
- 首先,RyC 检查各杂项文件及其权限,若失败则会立即诧异。
- 在子线程中检查 SSHD 服务状态,若正常则返回版本号。
- SSHD 服务正常启动后,RyC 启动在线控制系统。
- 循环接收用户输入。
- 使用 Z 关闭所有服务并退出进程。
新旧版本对比分析
相较于 SHW 能够校验 Hash、查看系统进程、四重并发的 C++ 启动器 “SSH蜜罐控制进程.exe”,RyC 似乎略显单薄。的确,不论是体量还是监控广度,SHW 都略胜一筹。
但 SHW 也有很多致命的缺陷,比如当攻击者退出蜜罐后,SHW 照例会暂停监控并询问用户是否关闭蜜罐(假设不关闭)。若在此期间攻击者进行连接,则会使 SHW 因检测不到 Bacsh 进程却能检测到用户输入而陷入死循环。
并且,“屏幕分辨率”、“进程名”、“终端格式”等诸多环境因素均会影响 SHW,降低了可移植性的同时,使其风险率大大升高。RyC 解决了上面的问题:通过接收本地用户输入,RyC 可以随时退出进程。同时,RyC 也在功能丰富度、灵活性、可移植性上面进行了全面提升,并且使用了独特的文件类型来记录用户数据,虽说 Web 界面还有一些限制(比如不支持手机端),但相比于 SHW,整体的进步还是十分显著的。
虽然目前两项目并不兼容,但由于 web_01.exe 支持自启动,所以用户只需自行更改文件路径便可使用 SHW 的启动器来启动此项目。
一些琐事
- RyC 曾与 RyB_Contol_Web,也就是 web_01.exe 是同一程序,后由于本人水平有限,无法实现并发,故拆分开来。
- RyC 下一版本可能会考虑加入控制子线程的本地命令
- RyC 在下一版本中可能会是重点发展对象,并且可能将 RyB 中的大部分功能(如发送警告邮件)包含进来,也许还会增加对 RyB 的控制,如修改路径等。这些操作旨在减少 RyB 潜在的风险隐患。
- 若本项目与 SHW 合并,则两种启动器都将被保留。实际上,它们更有可能作为一个全新启动器的两种内置模式供用户去选择。
- 本节标题寓意,“并驾齐驱”代表 RyC 支持并发,而“双管齐下”代表了本地控制系统和在线控制系统。
四. 十目所视,洞若观火
web_01.exe 程序简要概述
“某策善后,勇怯强弱,进退疾徐,洞若观火。” ——林潞
web_01.exe,在线控制系统,所属于RyB_Control_Web模块,
本节中简称为 RyCW。
虽说不怎么美观,但 RyCW 是本项目中唯一一个有头有脸的程序,它主要的 Web 页面由两部分构成:
- 页面左半部分是一些勾选框,它们用于控制攻击者在 RyB 中可用的命令(详见本节“ALCM 列表”小节)。
- 页面右半部分是三个显示框,分别显示:用户输入、系统输出和 RyB信息。
RyCW 的页面刷新频率是 1 次 / 3 秒。
RyCW 通常由 RyB_Control.exe 控制启动,但它也可以单独运行,其语法同 RyB_Control.exe 相同:
用法: web_01.exe <服务器 IP 地址> ...
RyCW 的勾选框可以自动地提交数据,当提交成功后,页面会显示如下:
危墙 在线控制系统 提交结果01 | 危墙 在线控制系统 提交结果02 |
RyCW 显示框很特别,它们的内容都是固定不变的,只有当页面刷新时,后端才会将新的内容 push 进去:
RyCW 处理 GET 请求部分源码分析
...
const HTML01: &'static str = r###" ... "###; // 将 Web 页面的 HTML 源码分块储存于常量中。
const HTML02: &'static str = r###" ... "###;
const HTML03: &'static str = r###" ... "###;
const HTML04: &'static str = r###" ... "###;
const HTML05: &'static str = r###" ... "###;
...
fn get_sys_data(tmp: &'static str, tmp1: usize) -> String
{
... // 读取 $tmp 路径指向的文件内容,将其从第 $tmp1 位至结尾的部分转化为 String 并返回。
}
fn get_form(_request: &mut Request) -> IronResult<Response> // 处理 GET 请求的函数
{
let mut response = Response::new();
response.set_mut(status::Ok); // 设置响应包的状态。
response.set_mut(mime!(Text/Html; Charset=Utf8)); // 设置响应包的网页编码。
response.set_mut(HTML01.to_string() // 设置响应包包含的页面。
+ &get_sys_data(USER_COMMAND, 9)
+ HTML02
+ &get_sys_data(SYSTEM_RESULT, 9)
+ HTML03
+ HTML04
+ &get_sys_data(SYSTEM_INFO, 14)
+ HTML05); // 将文件内容与 HTML 块拼接作为响应页面。
Ok(response)
}
RyCW 使用硬编码将 Web 页面的源码存储至常量中,后在前端页面自动刷新时,get_form() 函数依次读取指定历史文件的内容并将它们与这些常量拼接在一起作为返回值。
这是非常原始的方式,大部分时间都在做无用功,但作者的前端水平实在令人堪忧,无法做出一个更好的动态显示框,也不能让后端控制前端刷新,所以便采用了这个低级但有效的方法。
如果下一步能找到前端的合作开发者,我们会在未来版本改进这个瑕疵。
使用 RyCW 有两个弱点:
- 开放了 3000 号端口,增加了主机受到攻击的概率。
- 在没有任何防护的情况下,任何人都可以访问并更改 ALCM 列表。
以上的弱点如果不加以注意很有可能在生产中演化为非常大的麻烦。
作者建议最好为主机或网关加装一层带有深度数据包检查的 WAF,阻止出局域网的 HTTP 流量。同时,应有不少于两台计算机用于监测 RyCW。
上述经验是根据五月份的一次 PWK 得出的,下面是示意图(主机 SSHD 服务开启于 TCP/2000 端口):
经事后分析,我们均认为这次渗透测试促使我们形成了良好的防御体系。
ALCM(Allow_Command)列表
这是一个由 RyB_Control_Web 模块生成的列表,旨在控制 RyB 中可用的命令。
ALCM 列表储存于 OutPut01.alcm 文件中,其默认值为: g0g1g2g3g4
前端接收到用户输入(勾选框改变)时,目前所有被选中的勾选框所对应的值都会被传递给后端,RyCW 会提取并解析它们,将它们转化为 String 类型并输出至 OutPut01.alcm 文件。
以下是对应源码:
RyCW 解析 ALCM 列表部分源码分析
...
const HTML03: &'static str = r####"
...
<input type="checkbox", value="g0", name="gen" onclick="clicksubmit(this)" id="1">cd // 复选框的部分源码
<input type="checkbox", value="g1", name="gen" onclick="clicksubmit(this)" id="2">ls // 其中普通命令 name 为 “gen”,特殊命令 name 为 “warn”
<input type="checkbox", value="g2", name="gen" onclick="clicksubmit(this)" id="3">cat
<input type="checkbox", value="g3", name="gen" onclick="clicksubmit(this)" id="4">cls
<input type="checkbox", value="g4", name="gen" onclick="clicksubmit(this)" id="5">pwd
...
<input type="checkbox", value="w0", name="warn" onclick="clicksubmit(this)" id="9">touch
<input type="checkbox", value="w1", name="warn" onclick="clicksubmit(this)" id="10">mkdir
...
"####;
...
fn post_form(request: &mut Request) -> IronResult<Response> // 处理 POST 请求的函数
{
...
let form_data = match request.get_ref::<UrlEncodedBody>()
{
Err(_e) => ... ,
Ok(map) => map
};
...
let none_vec = vec![];
let gen_value = match form_data.get("gen") // 提取 name 为 “gen” 的数据的值 value
{
None => &none_vec,
Some(t) => t,
};
let warn_value = match form_data.get("warn")
{
None => &none_vec,
Some(t) => t,
};
...
let mut output = File::create(OUTPUT_PATH).unwrap(); // 打开文件流,准备写入。
let _ = write!(output, "{}\n{}", // 使用 write!() 宏覆盖式写入
gen_value.iter().map(|tmp|{tmp.to_string()}).collect::<String>(), // 转化为 String 类型
warn_value.iter().map(|tmp|{tmp.to_string()}).collect::<String>());
...
}
...
ALCM 列表对应于 RyB 中的 AllowCmd 结构体。
RyB 在执行命令前会首先解析 OutPut01.alcm 文件并将其传入 AllowCmd,大部分命令都对应一个 bool 值。
若用户输入匹配到某个命令,当且仅当这个命令对应的 AllowCmd 值为真时,RyB 才会调用对应的命令执行函数。
新旧版本对比分析
在 SHW 老版本中,与 RyCW 作用相同的是众多守护进程,如:CommandMonitor.exe 、ResultMonitor.exe。
和这些老古董比,RyCW 的优势算是非常明显了——既可以监视也可以操控,同时突破了物理限制,允许远程操作。
一些琐事
- 本模块的前身是一个关于在线计算器的网站,修改后纳入 Rusty_Borders 项目中。
- 下一版本中,RyCW 可能会推出移动端页面,可能会添加有关于系统服务的显示框。
- 本节标题寓意,“十目所视”代表 RyCW 支持多人协作,而“洞若观火”表明 RyCW 可以清晰刻画攻击者行为、动向。
五. Rusty_Borders 终端
“他大喊着:‘拉撒路,出来!’,然后拉撒路就真的走出了坟墓。”
一个简短的说明
本终端使用了 getch-rs 包,以期模拟 C 库函数 getch()。
但由于原 getch-rs 包对于 “Backspace 键”的识别有漏洞,所以作者使用 getch-rs.rs 替换了原包的 lib ,如若自行编译源码,请注意这一点。
正题之前:知耻而后勇——Rusty_Borders 的前世今生
Rusty_Borders 的祖先,也就是 SSH-Honeypot For Windows 在 2023 年的一次 PWK 中被攻破了,漏洞点是在内存方面。
当错误的地址被植入后,OS_Shell 就被启动了,红队也顺理成章地获得了反向 Shell。
经过检验、复盘、源码分析,这次渗透测试至少揭示以下三大缺陷:
- Bacsh 伪终端存在内存漏洞,这是由于用户可以欺骗过滤机制,使 C 库函数 memcpy() 和 strcpy() 的源和目标重叠。
- Bacsh 伪终端过度依赖使用 OS_Shell 执行用户命令,这为攻击者拿到反向 Shell 提供了条件。
- Bacsh 伪终端的代码冗余、复杂,并且做了过多不必要的限制。这使得代码可读性大大降低,项目维护和排查 Bug 非常困难,并且耗能严重。
说到底,“伪终端”,便是 Bacsh 的最大败笔。
除了上述缺陷,Bacsh 也存在着其他各式各样的问题,如:
- Bacsh 伪终端使用了 Linux 格式,其命令执行结果却是 PowerShell 格式,这使得它的整体风格十分丑陋,并且没有任何伪装。
- 退格问题和着色问题。
- 模块粘带
于是,在这样的背景下,写出一个真正的、内存安全的、高度仿真的 Windows 终端就变得日益重要。
而在二月的一次机缘巧合中,我接触到了 Rust——一种内存安全的系统编程语言,并把它当作了开始的契机。
- 三月十九日,这个项目确立了。万象更新,当时本项目的名称是 “Rust TermX (RTX)”,目标仅是编写一个高度契合 Linux 风格的终端,用来替换 Bacsh。
- 四月七日,在第二次内测时,经和其他开发者商议,作者决定将项目名更新为 “Rustic Terminus Tonic (RTT)”,目标更新为编写一个全新的 Win-SSH 蜜罐,同时使用在线 + 本地的方式进行控制和监测。
- 五月二十一日,在最后一次公测完成后,作者决定将项目名正式修改为与项目终端同名,也就是 “Rusty_Borders 危墙”。
Rust 语言很有特色,它的类型、生命周期、所有权、错误处理等机制为本项目提供了良好的技术根基。
同时,作者在 Bacsh 的算法基础上进行了大量的改进,使得本模块轻便但高效、灵活。
受到 Linux 内核中系统调用的启发,RyB 的命令执行函数均采用 “__ + 函数名” 的格式进行命名,如命令 cd
对用命令函数 __cd()。
本终端模块:
- 共经过 142 次大小测试,发布前经过 34 次测试,测试结果: 34 Ok,0 failed。
- 共发现 26 个漏洞,其中大型漏洞 8 个,发布前已全部修复。
- 目前包含 6 个子模块和 13 个命令模块,其中除 powershell 模块(见本节“特种命令——SU”小节)之外,所有模块均未发现漏洞风险。
下个版本(如果有)我会使用 config 文件代替硬编码来提高项目灵活性。
考虑到大多数同志电脑上都没有Rust环境,所以我这个版本就将 Config 做了出来。
大部分关键参数依然使用硬编码写入文件(如内置函数组、root 路径),唯一可能根据用户的改变而改变的参数就只有邮件发送的参数了。
默认安装路径:"C:\Users\Public\Control\Email_Config.cfg",这个cfg文件用于配置邮件发送。
过滤机制
RyB 使用类型为 [char; 25] 的数组 ac_command 来接收用户输入。
它的过滤机制应用了部分 Bacsh 的理念:
1.字符过滤
const FORBIDDEN_STRS: &'static str = r####"!"#$%&'()*+,;<=>?@[\]^_`{|}~"####;
const PERMITTED_STRS: &'static str = r####" -./0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"####;
RyB 的字符过滤函数更加简洁
- 对于 Char 类型,$FORBIDDEN_STRS 包含的字符是禁止的、仅 $PERMITTED_STRS 包含的字符是允许的。
- 对于 “BackSpace键”、“Delete键”、“\033[D”(左移光标) 和 “\033[C”(右移光标),RyB 可以将它们的效果打印出来并在数组中切换对应地址,但不会将这些字符本身记入命令数组。
- 其他控制字符均是禁止的。
- 对于空字符(EOF),RyB 会立即诧异。
若检测到禁止字符,则 RyB 会立即清空命令数组、清零计数器并删除屏幕上已打印的字段部分。 -
Z
用于退出 RyB 终端。
原因详见:
实际上,上述限制仅适用于 Utf-8 编码的字符,其他编码(如中文)则可能直接导致 RyB 诧异:
2.空格过滤
在接受用户输入的 “\r”( ENTER 键[1])后,若命令数组非空,则 RyB 会将数组中任意两个或两个以上的连续空格转化为一个空格。
这一步骤是依靠闭包的递归迭代实现的:
RyB 闭包递归部分源码分析
...
let mut ac_command: [char; 25] = ['\0'; 25];
...
struct Space<'s> { space_01: &'s dyn Fn(&Space, [char; 25], usize, usize) -> ([char; 25], usize, usize) } // 定义包含动态元素的结构体
let nullspace_iter = Space
{
space_01: &|nullspace_iter, mut ac_command, tmp, mut counter_01| // 在结构体中定义闭包
{
if ac_command[tmp] == ' '
&& ac_command[tmp + 1] == ' '
{
for tmp1 in tmp .. counter_01
{
ac_command[tmp1] = ac_command[tmp1 + 1];
}
ac_command[counter_01] = '\0';
counter_01 -= 1;
return (nullspace_iter.space_01)(nullspace_iter, ac_command, tmp, counter_01); // 递归迭代结构体
}
(ac_command, tmp + 1, counter_01)
},
};
let mut tmp: usize = 0;
loop
{
(ac_command, tmp, counter_01) = (nullspace_iter.space_01)(&nullspace_iter, ac_command.clone(), tmp, counter_01.clone()); // counter_01 为 ac_command 中有效元素的个数
if tmp >= counter_01
{
break;
}
}
...
Rust 不支持闭包直接进行递归。
不仅如此,您会发现函数的调用可以先于它的定义存在,而闭包必须先进行定义才能使用。
这是很正常的,闭包与环境的关系太过紧密,在没有完整定义的情况下,内存的分配是未知的。
而若使用子函数,则需要传递大量的环境变量,得不偿失了。
目前实现闭包递归最流行的方法有两种,第一种是 Y 组合子这种奇淫巧计。
但很不幸,由于作者对于妖魔鬼怪一窍不通,所以我选择第二种:Rust 结构体。
递归的本质是要在栈上操作一层一层的参数,为了避免所有权纷争,我们不传引用。
我们先使用关键字 dyn 定义一个包含了动态元素的结构体 Space,生命期为 's。
其中,动态元素的类型、格式对应于闭包的类型和格式:Fn(&Space, [char; 25], usize, usize) -> ([char; 25], usize, usize)
。
注意闭包接受的参数中必须有一个 Space 类型的引用。
接下来将 Space 类型赋给变量 $nullspace_iter,并且定义闭包的内容。
现在,$nullspace_iter 的动态元素和闭包是等价的了。
您会发现,上述代码中没有直接出现闭包,传递的都是结构体。
这样做的意义在于递归时调用的是结构体的元素:
这样,闭包的结构就完整了。
通过结构体这个媒介,我们将闭包的递归变为了结构体动态元素的递归。
3.语句识别
在过滤完成空格后,会进入语句识别阶段。
-
OS_Shell
Bacsh OS_Shell 组包括:“cmd”、“powershell”、“exe”、“runas”以及它们的任意大小写组合。
若识别到 OS_Shell 组,则会打印指定提示语(访问被拒绝)。
若 OS_Shell 组中的函数名前后有任意数字或字母,则不会出触发识别机制。 -
内置函数
RyB 的内置函数组包括:“cat”、“cd”、“cls”、“help”、“id”、“ls”、“mkdir”、“pwd”、“rm”、“su”、“touch”、“whoami”、“wipe”以及它们的任意大小写组合。
其中,“cd”函数模块包含目录解析函数,是其他大部分函数的基础。
若内置函数组中的函数名前后有任意数字或字母,或不位于句首,则不会出触发识别机制。
若用户输入匹配到内置函数组,并且这个命令对应的 AllowCmd 值为真时,RyB 会调用对应的命令执行函数处理用户请求。 -
其他任何语句都被视为无效语句,返回信息“命令未找到。”。
特种命令——SU
作者纠结了很长时间要不要把这个模块发出来,后来出于学术交流和项目完整性的考量还是发了。
这个命令函数重要而危险,以至于它需要单独占一小节。
这是在 SU 命令的 help 界面:
SU 0.1.13
Copyright (TM) 2022-2026 Двигатель Ржавчина, All Rights Reserved.
在用户拥有开发者许可证的情况下,可以使用本命令来提升权限至任意等级。
目前共三等权限:
ONLY_MEMBER => ·任何命令都是可用的;
·不受到Allow_CMD命令列表的管制;
·可查看真实路径;
·可启动OS_Shell。
root => 除su命令外,其他命令都是可用的,但受到Allow_CMD
的约束。
kali => 无法使用wipe等可修改原环境文件的命令,受到
Allow_CMD的约束。
*为保证环境可控,任何权限修改仅能影响回显类命令的操作结果,如whoami、
id、pwd等,而诸如目录变迁、文件读写等会改变环境状态的命令则均不会收
到影响,如cd、touch、ls等。
*请注意su的提权并不是在子Shell中进行的。
本命令应仅在测试阶段使用。
若要禁止su命令,仅需移除License许可文件即可。
用法:su [用户名 USER]...
参数:无。
Written by Jessarin000.
描述的很清楚了,这个命令是有很大危险性的,不过对于攻击者来说,这是一个很好的切入点,所以或许可以以其人之道来对付它们。
在目前的测试当中,SU 命令未发现漏洞。所以只要许可证配置无误,我们完全可以使用这个“饵”来钓“鱼”。
关于上面所说的“可启动 OS_Shell”这点,作者专门写了一个 powershell 模块,这是在项目调试阶段时使用的,可以单句执行 OS_Shell。
这个模块已停止维护,下一版本可能会移除。
邮件发送系统
在 RyB 运行过程中,有时因为种种原因(如文件权限配置不当、目录丢失、进程抢占等)使得即使发生了错误也无法写入历史文件。
在这种情况下,RyB 就会向 Email_Config.cfg 文件中配置的邮箱地址发送一封警告邮件,里面包含了错误等级、时间、连接 IP和错误原因。
下面是一张样图:
RyB 采用经典的 lettre 包进行邮件发送。
在启动 RyB 之前请配置 Email_Config.cfg,否则会导致诧异。
Email_Config.cfg 的格式:
#Email 配置文件
#############################
# 注意,不要在“=”前后加空格 #
#############################
# 你的邮箱:
TAGO=
# 还是你的邮箱:
SOSR=
# SMTP服务器,如smtp.qq.com:
SMTP=
# 邮箱授权码:
PAWD=
许可证系统 License
License 是 RyB 的特色功能之一,被设计于标识用户身份 以及 控制特种命令。
其中,标识用户身份 的功能仅在内测中被启用。
License 记载于 License.txt 文件中,默认许可证为:BSTLL-2446-3368-0157-7308-92DBC-001
。
License 许可证规则如下:
// 这是许可证的规则
1.
ASTLL => Never_Expires ::永不过期
BSTLL => Valid_for_One_Year ::一年有效
CSTLL => Valid_for_Three_Month ::三个月有效
ASR => Temporary_License ::临时
2.
92DSC => Project_Developer ::项目开发者
92DBC => Beta_Participants ::内测参与者
00000 => User ::用户
ASTLL 0000 3368 0157-7308 92DSC 001
有效期 启动日期 设备编号01 伪码 身份 设备编号02
许可证规则及示例均可以在 License_key.txt 文件中找到。
许可证系统可以配合 SU 命令,用于吸引攻击者。
事实上,在第一次公测中,SU 便成为了众矢之的——大多数同志或多或少都希望从这个方向打开突破口。
正如前文所述,这是“一把指向自己的利剑”。
* 注意:License 系统仅适用于 RyB 终端,与本项目许可证 LICENCE 无任何瓜葛。
新旧版本对比分析
虽然 RyB 不论是在“机制”上还是在“策略”上都强于 Bacsh,但 Bacsh 的意义仍旧是非凡的,它确立了 Win-SSH 蜜罐的理念,并且包含了大部分的算法,促进了整个早期体系的形成。
不论差别有多大,RyB 身上都深深打下了 Bacsh 的烙印。
后记
本项目遵循 GPL v2.0 协议。
本项目允许使用者修改、移植并再发布源码,但“当你发布它的时候,请确保你的项目使用者享有你曾经拥有的所有权力”。
让我们看到你的创意:D。
本项目或将推出 Linux 版本。
本项目耗时两个月完成,比原先的两个项目要久一点。
维护与更新工作可能需要一段时间才能提上日程,作者需要休息一段时间,倒倒时差,写写作业,过一个大学生应该过的生活(doge)。
希望本项目对你有用 ;-)
杂项文件列表
文件名 | 默认目录 | 功能 |
---|---|---|
RyB_INFO.ryb | C:\Users\Public\History | 记录 RyB 信息 |
SYSTEM_RESULT.ryb | C:\Users\Public\History | 记录系统输出 |
USER_COMMAND.ryb | C:\Users\Public\History | 记录用户输入 |
License.txt | C:\Users\Public\Control | 包含许可证 |
License_key.txt | C:\Users\Public\Control | 包含许可证规则、示例 |
OutPut01.alcm | C:\Users\Public\Control | 存储 ALCM 列表 |
Email_Config.cfg | C:\Users\Public\Control | 包含邮件配置 |
style.css | C:\Users\Public\SSHShell\Web_Control | Web 组件01 |
rust.ico | C:\Users\Public\SSHShell\Web_Control | Web 组件02 |
Rost.txt | C:\Users\Public\SSHShell\Web_Control | Web 组件03 |
pic.html | C:\Users\Public\SSHShell\Web_Control | Web 组件04 |
Jessarin000
2024-05-30 作
[1] 由于使用是 Char 类型,仅接收单个字符,故在 ENTER 键的 ‘\r\n’ 中仅能识别 “\r” 。 ↩
-
危墙——由 Rust 语言编写的新一代 Windows SSH 蜜罐
- 这篇文章包含章节为:
- 简介
- 项目链接: Rusty_Borders 危墙
- 更新日志:项目内容与亮点
- 一.构件
- 它是怎么实现的?
- 二.获取
- 如何部署/启动它?
- 三. 并驾齐驱,双管齐下
- RyB_Control.exe 程序简要概述
- RyC 接收部分源码分析
- RyC 并发部分源码分析
- 新旧版本对比分析
- 一些琐事
- 四. 十目所视,洞若观火
- web_01.exe 程序简要概述
- RyCW 处理 GET 请求部分源码分析
- ALCM(Allow_Command)列表
- RyCW 解析 ALCM 列表部分源码分析
- 新旧版本对比分析
- 一些琐事
- 五. Rusty_Borders 终端
- 一个简短的说明
- 正题之前:知耻而后勇——Rusty_Borders 的前世今生
- 过滤机制
- RyB 闭包递归部分源码分析
- 特种命令——SU
- 邮件发送系统
- 许可证系统 License
- 新旧版本对比分析
- 后记
- 杂项文件列表