newListeners = new ArrayList<>();
473 | for (Object o : listeners) {
474 | if (o instanceof ServletRequestListener) {
475 | newListeners.add((ServletRequestListener) o);
476 | }
477 | }
478 |
479 | // Scan listener
480 | out.write("Listener scan result
");
481 |
482 | out.write("\n" +
483 | " \n" +
484 | " ID | \n" +
485 | " Listener class | \n" +
486 | " Listener classLoader | \n" +
487 | " Listener class file path | \n" +
488 | " dump class | \n" +
489 | " kill | \n" +
490 | " \n" +
491 | " ");
492 |
493 | int index = 0;
494 | for (ServletRequestListener listener : newListeners) {
495 | out.write("");
496 | out.write(String.format("%d | %s | %s | %s | dump | kill | "
497 | , index + 1
498 | , listener.getClass().getName()
499 | , listener.getClass().getClassLoader()
500 | , classFileIsExists(listener.getClass())
501 | , listener.getClass().getName()
502 | , listener.getClass().getName()));
503 | out.write("
");
504 | index++;
505 | }
506 | out.write("
");
507 |
508 |
509 |
510 |
511 | out.write("Tomcat-Value scan result
");
512 | out.write("说明:正常情况下只有两个(不要杀错!!!!)。查杀常规流程,看类、加载类、dump下来分析
");
513 | out.write("\n" +
514 | " \n" +
515 | " ID | \n" +
516 | " Tomcat-Value class | \n" +
517 | " Tomcat-Value classLoader | \n" +
518 | " Tomcat-Value class file path | \n" +
519 | " dump class | \n" +
520 | " kill | \n" +
521 | " \n" +
522 | " ");
523 |
524 | for (int i = 0; i < valves.length; i++) {
525 | out.write("");
526 | out.write(String.format("%d | %s | %s | %s | dump | kill | "
527 | , i
528 | , valves[i]
529 | ,valves[i].getClass().getClassLoader()
530 | , classFileIsExists(valves[i].getClass())
531 | ,valves[i].getClass().getName()
532 | , i));
533 | out.write("
");
534 | }
535 | out.write("
");
536 |
537 |
538 | //Timer scan
539 | out.write("Timer scan result
");
540 | out.write("说明:Java定时任务实现的内存马,通常需要多线程发包才能执行命令(一般无回显)。查杀常规流程,看类、加载类、dump下来分析
");
541 | out.write("\n" +
542 | " \n" +
543 | " ID | \n" +
544 | " Thread Name | \n" +
545 | " TimerTask Class | \n" +
546 | " TimerTask classLoader | \n" +
547 | " dump class | \n" +
548 | " kill | \n" +
549 | " \n" +
550 | " ");
551 | Thread[] threads = (Thread[]) ((Thread[]) getField(Thread.currentThread().getThreadGroup(), "threads"));
552 | int ii =0;
553 | for (Thread thread : threads) {
554 | if (thread != null) {
555 | if("java.util.TimerThread".equals(thread.getClass().getName())){
556 |
557 | Class clzTimerThread = thread.getClass();
558 | Field queueField = clzTimerThread.getDeclaredField("queue");
559 | queueField.setAccessible(true);
560 | //Timer里面的TaskQueue()对象
561 | Object queue = queueField.get(thread);
562 |
563 | Class clzTaskQueue = queue.getClass();
564 | Method getTimeTask = clzTaskQueue.getDeclaredMethod("get",int.class);
565 | getTimeTask.setAccessible(true);
566 | //从TaskQueue对象中获取TimerTask,然后取消这个TimerTask以清除任务。
567 | TimerTask timerTask = (TimerTask) getTimeTask.invoke(queue,1);
568 | if(timerTask!=null){
569 | //timerTask.cancel();
570 | out.write("");
571 | out.write(String.format("%d | %s | %s | %s | dump | kill | "
572 | , ii
573 | , thread.getName()
574 | , timerTask.getClass().getName()
575 | , timerTask.getClass().getClassLoader()
576 | , timerTask.getClass().getName()
577 | , thread.getName()));
578 | out.write("
");
579 | ii++;
580 | }
581 | }
582 | }
583 | }
584 | out.write("
");
585 |
586 |
587 |
588 |
589 | out.write("Websocket scan result
");
590 | out.write("说明:把他看作Servlet即可,如果当前服务没有用到websocket,这里出现了,那必是内存马。
");
591 | out.write("\n" +
592 | " \n" +
593 | " ID | \n" +
594 | " Websocket Name | \n" +
595 | " Patern | \n" +
596 | " Websocket class | \n" +
597 | " Websocket classLoader | \n" +
598 | " dump class | \n" +
599 | " kill | \n" +
600 | " \n" +
601 | " ");
602 |
603 | // 通过 request 的 context 获取 ServerContainer
604 | WsServerContainer wsServerContainer = (WsServerContainer) request.getServletContext().getAttribute(ServerContainer.class.getName());
605 |
606 | // 利用反射获取 WsServerContainer 类中的私有变量 configExactMatchMap
607 | Class> obj = Class.forName("org.apache.tomcat.websocket.server.WsServerContainer");
608 | Field field = obj.getDeclaredField("configExactMatchMap");
609 | field.setAccessible(true);
610 | Map configExactMatchMap = (Map) field.get(wsServerContainer);
611 |
612 | // 遍历configExactMatchMap, 打印所有注册的 websocket 服务
613 | Set keyset = configExactMatchMap.keySet();
614 | Iterator iterator = keyset.iterator();
615 |
616 |
617 | int j = 0;
618 | while (iterator.hasNext()) {
619 | String key = iterator.next();
620 | Object object = wsServerContainer.findMapping(key);
621 | Class> wsMappingResultObj = Class.forName("org.apache.tomcat.websocket.server.WsMappingResult");
622 | Field configField = wsMappingResultObj.getDeclaredField("config");
623 | configField.setAccessible(true);
624 | ServerEndpointConfig config1 = (ServerEndpointConfig)configField.get(object);
625 | Class> clazz = config1.getEndpointClass();
626 |
627 |
628 |
629 | out.write("");
630 | out.write(String.format("%d | %s | %s | %s | %s | dump | kill | "
631 | , j
632 | , clazz.getSimpleName()
633 | , key
634 | ,clazz.getName()
635 | ,clazz.getClassLoader()
636 | ,clazz.getName()
637 | , key));
638 | out.write("
");
639 |
640 | j++;
641 | }
642 |
643 | out.write("
");
644 |
645 |
646 |
647 | out.write("Upgrade scan result
");
648 | out.write("说明:通常情况下不存在Upgrade,有结果就99%是内存马了。剩下1%dump下来分析流程确定吧。
");
649 | out.write("\n" +
650 | " \n" +
651 | " ID | \n" +
652 | " Upgrade Name | \n" +
653 | " Key | \n" +
654 | " Upgrade class | \n" +
655 | " Upgrade classLoader | \n" +
656 | " dump class | \n" +
657 | " kill | \n" +
658 | " \n" +
659 | " ");
660 |
661 | Request request1 =(Request) getField(request, "request");;
662 | Connector realConnector = (Connector) getField(request1, "connector");
663 | AbstractHttp11Protocol handler = (AbstractHttp11Protocol) getField(realConnector, "protocolHandler");
664 | HashMap upgradeProtocols = (HashMap) getField(handler,"httpUpgradeProtocols");
665 |
666 |
667 | int aaa = 0;
668 | for (Map.Entry entry : upgradeProtocols.entrySet()) {
669 |
670 |
671 | out.write("");
672 | out.write(String.format("%d | %s | %s | %s | %s | dump | kill | "
673 | , aaa
674 | , entry.getValue().getClass().getSimpleName()
675 | , entry.getKey()
676 | , entry.getValue().getClass().getName()
677 | , entry.getValue().getClass().getClassLoader()
678 | , entry.getValue().getClass().getName()
679 | , entry.getKey()));
680 | out.write("
");
681 | aaa++;
682 |
683 | }
684 | out.write("
");
685 |
686 |
687 |
688 |
689 |
690 |
691 | out.write("ExecutorShell Check
");
692 | out.write("说明:通过修改nioEndpoint中存储的Executor线程池对象为恶意对象实现的内存马。因此查杀起来简单方便,看他对应的类是不是原本的org.apache.tomcat.util.threads.ThreadPoolExecutor即可
");
693 | out.write("\n" +
694 | " \n" +
695 | " Executor Name | \n" +
696 | " Executor class | \n" +
697 | " Executor classLoader | \n" +
698 | " dump class | \n" +
699 | " 恢复 | \n" +
700 | " \n" +
701 | " ");
702 |
703 |
704 | // 从线程中获取NioEndpoint类
705 | NioEndpoint nioEndpoint = null;
706 | try {
707 | nioEndpoint = (NioEndpoint) getNioEndpoint();
708 | } catch (Exception e) {
709 | e.printStackTrace();
710 | }
711 | ThreadPoolExecutor executor = (ThreadPoolExecutor)nioEndpoint.getExecutor();
712 |
713 |
714 | out.write("");
715 | out.write(String.format("%s | %s | %s | dump | kill | "
716 | , executor.getClass().getSimpleName()
717 | , executor.getClass().getName()
718 | , executor.getClass().getClassLoader()
719 | , executor.getClass().getName()
720 | , "recovery"));
721 | out.write("
");
722 | out.write("
");
723 |
724 |
725 |
726 |
727 |
728 |
729 | }
730 |
731 |
732 | %>
733 |
734 |
735 | code by c0ny1;
736 |
737 |
738 |
739 |
--------------------------------------------------------------------------------