\[ \definecolor{data}{RGB}{18,110,213} \definecolor{unknown}{RGB}{217,86,16} \definecolor{learned}{RGB}{175,114,176} \]

Jak nie robić kryptografii

Jarosław Jedynak

Intro

$ whois msm

Security BSides 2017

Temat: Czego nie robić z kryptografią

Security BSides 2017

Temat: Czego nie robić z kryptografią Temat 2: Czego nie robić ze StackOverflow

Security BSides 2017

Temat: Czego nie robić z kryptografią Temat 2: Czego nie robić ze StackOverflow Bonus: Wyśmiewanie się z niewinnych ludzi

Security BSides 2017

Temat: Czego nie robić z kryptografią Temat 2: Czego nie robić ze StackOverflow Bonus: Wyśmiewanie się z niewinnych ludzi
Bonus: Poprawianie ludzi w dobrej wierze

Agenda

  • Szyfry blokowe
  • Funkcje skrótu
  • Własna kryptografia

Agenda

  • Szyfry blokowe
    • 3 minuty teorii
    • Tryb ECB
    • Tryb CTR (bonus)
    • Tryb CBC
  • Funkcje skrótu
  • Własna kryptografia

Agenda

  • Szyfry blokowe
  • Funkcje skrótu / podpisy
    • Co to funkcje skrótu
    • Jak ich źle użyć
    • Co to podpisy
    • Jak ich źle użyć
  • Własna kryptografia

Agenda

  • Szyfry blokowe
  • Funkcje skrótu / podpisy
  • Własna kryptografia
    • Co to własna kryptografia
    • Jak ją złamać

Szyfry blokowe

Szyfry wszelakie

Szyfry asymetryczne

  • Matematyczna czarna magia
  • Bardzo trudno poprawnie użyć
  • Dlatego robią to poprawnie biblioteki
  • Największe osiągnięcie kryptografii
  • Np. RSA, Diffie-Hellman albo krzywe eliptczne
  • O nich tu nie będzie.

Szyfry strumieniowe

def stream_cipher(data, key):
    pseudo_random_bytes = generate_bytes(key)
    result = ''
    for i in range(len(data))
        result += data[i] ^ pseudo_random_bytes[i]
    return result

Cechy charakterystyczne:

  • Szyfrowanie bit po bicie (bajt po bajcie).
  • Sprowadza się do xorowania z losowym keystreamem.

Przykłady to np. RC4, Salsa20, ChaCha, Spritz

Szyfry blokowe

def block_cipher(data, key):
    blocks = chunks(data, BLOCK_SIZE)
    result = ''
    for block in blocks:
        result += raw_cipher(block, key)
    return result

Cechy charakterystyczne:

  • Szyfrowanie blok po bloku - np. po 16 bajtów.
  • Samo szyfrowanie bloku to permutacja bajtów.

Przykłady to np. 3DES, AES, Blowfish

Padding pkcs#7

Mamy 16bajtowe bloki i 39bajtową wiadomość. Co robić?

Tryby wiązania bloków

Mamy 16bajtowe bloki i 48bajtową wiadomość. Co robić?

Tryb ECB

Tryb ECB

ct[0] = aes(pt[0])
ct[1] = aes(pt[1])
ct[2] = aes(pt[2])

Story time

Aplikacja webowa, trzymająca informacje o użytkowniku w ciasteczkach. Format:

{
    "name": "msm",
    "has_admin": false
}

Kod:

cipher = AES.new(SECRET_KEY)
raw = pkcs7_pad(raw_cookie)
return cipher.encrypt(raw).encode('hex')

Tryb ECB

Przykładowa zaszyfrowana wiadomość.

Użytkownik: msm

Czy jest tu potencjalnie jakiś problem?

Tryb ECB

Użytkownik: hacker

Użytkownik: hackertrue___________________x

Tryb ECB

Tryb ECB


{
    "name": "hacker",
    "has_admin": true
}

Tryb ECB - Overkill

Co myśli o tym internet

Wujek StackOverflow

https://stackoverflow.com/questions/606179/what-encryption-algorithm-is-best-for-encrypting-cookies

function encrypt($text, $salt) { 
    return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text,
        MCRYPT_MODE_ECB, mcrypt_create_iv(
            mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
            MCRYPT_RAND)))); 
}

function decrypt($text, $salt) { 
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, base64_decode($text),
        MCRYPT_MODE_ECB, mcrypt_create_iv(
            mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
            MCRYPT_RAND))); 
}

Po pierwsze

1. sam pomysł szyfrowania cookies jest głupi

function encrypt($text, $salt) { 
    return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text,
        MCRYPT_MODE_ECB, mcrypt_create_iv(
            mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
            MCRYPT_RAND)))); 
}

function decrypt($text, $salt) { 
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, base64_decode($text),
        MCRYPT_MODE_ECB, mcrypt_create_iv(
            mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
            MCRYPT_RAND))); 
}

Po drugie

2. MCRYPT_MODE_ECB + mcrypt_create_iv = ?

function encrypt($text, $salt) { 
    return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text,
        MCRYPT_MODE_ECB, mcrypt_create_iv(
            mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
            MCRYPT_RAND)))); 
}

function decrypt($text, $salt) { 
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, base64_decode($text),
        MCRYPT_MODE_ECB, mcrypt_create_iv(
            mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
            MCRYPT_RAND))); 
}

Po drugie

Tryb ECB z IV?

PHP jest zbyt cool, żeby się przejmować

(a warningi są dla słabych)

Po trzecie

3. This is not the encryption algorithm you're looking for

function encrypt($text, $salt) { 
    return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text,
        MCRYPT_MODE_ECB, mcrypt_create_iv(
            mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
            MCRYPT_RAND)))); 
}

function decrypt($text, $salt) { 
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, base64_decode($text),
        MCRYPT_MODE_ECB, mcrypt_create_iv(
            mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
            MCRYPT_RAND))); 
}

Po trzecie

MCRYPT_RIJNDAEL_256

PHP jest zbyt cool, żeby używać mainstreamowych algorytmów.

(MCRYPT_RIJNDAEL_256 to mało zbadana, niszowa odmiana AES z 32bajtowymi blokami)

Po czwarte

4. $salt? trim?

function encrypt($text, $salt) { 
    return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text,
        MCRYPT_MODE_ECB, mcrypt_create_iv(
            mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
            MCRYPT_RAND)))); 
}

function decrypt($text, $salt) { 
    return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, base64_decode($text),
        MCRYPT_MODE_ECB, mcrypt_create_iv(
            mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
            MCRYPT_RAND))); 
}

The StackOverflow Effect

The StackOverflow Effect

Przykład A: Pierwszy z brzegu

/albracu/software_venta_php/blob/master/core/Auth.php

private static function encryptCookie(string $value) : string {
    $key = self::aud();
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    return mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $value, MCRYPT_MODE_ECB, $iv);
}

public static function signIn(array $data) {
    $time = time() + ( 3600 * ServicesContainer::getConfig()['session-time'] );
    setcookie(
        ServicesContainer::getConfig()['session-name'],
        Auth::encryptCookie( serialize($data) ),
        $time, '/'
    );
}

Przykład B: SO Szkodzi Zdrowiu

/chrislynch/engine4/blob/master/_e/plugins/cart/cart.php

public static function addToCartFormValue($Code,$Price,$QTY = 1,$Title = '',$Image = '',$Other = array()){
    $data = array();
    $data['Code'] = $Code;
    $data['Price'] = $Price;
    $data['QTY'] = $QTY;
    if ($Title == ''){ $data['Title'] = $Code; } else { $data['Title'] = $Title; }
    $data['Image'] = $Image;
    foreach($Other as $otherKey=>$otherValue){
        $data[$otherKey] = $otherValue;
    }
    include_once('_e/plugins/cryptography/cryptography.php');
    return _cryptography::encrypt(serialize($data));

}

Przykład B: SO Szkodzi Zdrowiu

Tytuł test

a:4:{s:5:"Title";s:4:"test";s:4:"Code";s:7:"code123";s:5:"Price";i:100;s:3:"QTY";i:1;}

Bloki:

a:4:{s:5:"Title"
;s:4:"test";s:4:
"Code";s:7:"code
123";s:5:"Price"
;i:100;s:3:"QTY"
;i:1;}

Przykład B: SO Szkodzi Zdrowiu

Tytuł Title____;Price";i:001;s:3

    a:4:{s:5:"Title";s:26:"Title____;Price";i:001;s:3";s:4:"Code";s:7:"code123";s:5:"Price";i:100;s:3:"QTY";i:1;}

Bloki:

a:4:{s:5:"Title"
;s:26:"Title____
;Price";i:001;s:
3";s:4:"Code";s:
7:"code123";s:5:
"Price";i:100;s:
3:"QTY";i:1;}

Przykład B: SO Szkodzi Zdrowiu

Tytuł Title____;Price";i:001;s:3

array(4) {
  ["Title"]=>
  string(26) "Title____;Price";i:001;s:3"
  ["Code"]=>
  string(7) "code123"
  ["Price"]=>
  int(100)
  ["QTY"]=>
  int(1)
}

a:4:{s:5:"Title"
;s:26:"Title____
;Price";i:001;s:
3";s:4:"Code";s:
7:"code123";s:5:
"Price";i:100;s:
3:"QTY";i:1;}

Przykład B: SO Szkodzi Zdrowiu

Tytuł Title____;Price";i:001;s:3

array(4) {
  ["Title"]=>
  string(26) "Title____;Price";i:001;s:3"
  ["Code"]=>
  string(7) "code123"
  ["Price"]=>
  int(1)
  ["QTY"]=>
  int(1)
}

a:4:{s:5:"Title"
;s:26:"Title____
;Price";i:001;s:
3";s:4:"Code";s:
7:"code123";s:5:
"Price";i:001;s:
3:"QTY";i:1;}

Przykład C: Arcydzieło kryptografii

Przykład C: Arcydzieło kryptografii

function encrypt($str){
    if (!$this->key) { return $str; }
    $enc_str = '';
    if (!$this->_check_mcrypt()) {
        // non-mcrypt basic encryption
        for ($i = 0; $i < strlen($str); $i++){
            $char = substr($str, $i, 1);
            $keychar = substr($this->key, ($i % strlen($this->key)) - 1, 1);
            $enc_str .= chr(ord($char) + ord($keychar));
        }
        $enc_str = base64_encode($enc_str);
    } else{
        $hash = hash('sha256', $this->key, true);
        $enc_str = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $hash,
            $str, MCRYPT_MODE_ECB));
    }
    return str_replace('==', '', strtr($enc_str, '+/', '-_'));
} 

Przykład C: Arcydzieło kryptografii

function encrypt($str){
    if (!$this->key) { return $str; }
    $enc_str = '';
    if (!$this->_check_mcrypt()) {
        // non-mcrypt basic encryption
        for ($i = 0; $i < strlen($str); $i++){
            $char = substr($str, $i, 1);
            $keychar = substr($this->key, ($i % strlen($this->key)) - 1, 1);
            $enc_str .= chr(ord($char) + ord($keychar));
        }
        $enc_str = base64_encode($enc_str);
    } else{
        $hash = hash('sha256', $this->key, true);
        $enc_str = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $hash,
            $str, MCRYPT_MODE_ECB));
    }
    return str_replace('==', '', strtr($enc_str, '+/', '-_'));
} 

Przykład C: Arcydzieło kryptografii

function encrypt($str){
    if (!$this->key) { return $str; }
    $enc_str = '';
    if (!$this->_check_mcrypt()) {
        // non-mcrypt basic encryption
        for ($i = 0; $i < strlen($str); $i++){
            $char = substr($str, $i, 1);
            $keychar = substr($this->key, ($i % strlen($this->key)) - 1, 1);
            $enc_str .= chr(ord($char) + ord($keychar));
        }
        $enc_str = base64_encode($enc_str);
    } else{
        $hash = hash('sha256', $this->key, true);
        $enc_str = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $hash,
            $str, MCRYPT_MODE_ECB));
    }
    return str_replace('==', '', strtr($enc_str, '+/', '-_'));
} 

Przykład C: Arcydzieło kryptografii

function encrypt($str){
    if (!$this->key) { return $str; }
    $enc_str = '';
    if (!$this->_check_mcrypt()) {
        // non-mcrypt basic encryption
        for ($i = 0; $i < strlen($str); $i++){
            $char = substr($str, $i, 1);
            $keychar = substr($this->key, ($i % strlen($this->key)) - 1, 1);
            $enc_str .= chr(ord($char) + ord($keychar));
        }
        $enc_str = base64_encode($enc_str);
    } else{
        $hash = hash('sha256', $this->key, true);
        $enc_str = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $hash,
            $str, MCRYPT_MODE_ECB));
    }
    return str_replace('==', '', strtr($enc_str, '+/', '-_'));
} 

Przykład C: Arcydzieło kryptografii

function encrypt($str){
    if (!$this->key) { return $str; }
    $enc_str = '';
    if (!$this->_check_mcrypt()) {
        // non-mcrypt basic encryption
        for ($i = 0; $i < strlen($str); $i++){
            $char = substr($str, $i, 1);
            $keychar = substr($this->key, ($i % strlen($this->key)) - 1, 1);
            $enc_str .= chr(ord($char) + ord($keychar));
        }
        $enc_str = base64_encode($enc_str);
    } else{
        $hash = hash('sha256', $this->key, true);
        $enc_str = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $hash,
            $str, MCRYPT_MODE_ECB));
    }
    return str_replace('==', '', strtr($enc_str, '+/', '-_'));
} 

Przykład C: Arcydzieło kryptografii

function encrypt($str){
    if (!$this->key) { return $str; }
    $enc_str = '';
    if (!$this->_check_mcrypt()) {
        // non-mcrypt basic encryption
        for ($i = 0; $i < strlen($str); $i++){
            $char = substr($str, $i, 1);
            $keychar = substr($this->key, ($i % strlen($this->key)) - 1, 1);
            $enc_str .= chr(ord($char) + ord($keychar));
        }
        $enc_str = base64_encode($enc_str);
    } else{
        $hash = hash('sha256', $this->key, true);
        $enc_str = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $hash,
            $str, MCRYPT_MODE_ECB));
    }
    return str_replace('==', '', strtr($enc_str, '+/', '-_'));
} 

Przykład C: Arcydzieło kryptografii

function encrypt($str){
    if (!$this->key) { return $str; }
    $enc_str = '';
    if (!$this->_check_mcrypt()) {
        // non-mcrypt basic encryption
        for ($i = 0; $i < strlen($str); $i++){
            $char = substr($str, $i, 1);
            $keychar = substr($this->key, ($i % strlen($this->key)) - 1, 1);
            $enc_str .= chr(ord($char) + ord($keychar));
        }
        $enc_str = base64_encode($enc_str);
    } else{
        $hash = hash('sha256', $this->key, true);
        $enc_str = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $hash,
            $str, MCRYPT_MODE_ECB));
    }
    return str_replace('==', '', strtr($enc_str, '+/', '-_'));
} 

Przykład C: Arcydzieło kryptografii

2x bezpieczniejsza wersja poprzedniej funkcji:

function encrypt($str) {
    return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $this->key,
        $str, MCRYPT_MODE_ECB));
} 

(Już nawet nie wspominając o trybie ECB)

Przykład C: Arcydzieło kryptografii

A jak to wszystko jest użyte?

$cutomer_data = $encryption->encrypt(serialize(array(
        'first_name'  => $this->firstname,
        'customer_id' => $this->customer_id,
        'script_name' => $this->request->server['SCRIPT_NAME']
)));
setcookie('customer', $cutomer_data, time() + 60 * 60 * 24 * 365, '/',
    $this->request->server['HTTP_HOST']);

Patrz kilka slajdów wcześniej, co się da zrobić z serialize i szyfrowaniem ECB.

Przykład C: Arcydzieło kryptografii

To wszystko w dość popularnym rozwiązaniu eCommerce:

Podsumowanie

Wnioski na razie:

  • Nie trzymać żadnych wrażliwych danych w ciastkach
  • Jeśli już, to nie polegać na ich szyfrowaniu
  • Nie używać trybu ECB do niczego
  • Nie pisać w PHP (opcjonalne)

Podsumowanie

Wnioski na razie:

  • Nie trzymać żadnych wrażliwych danych w ciastkach
  • Jeśli już, to nie polegać na ich szyfrowaniu
  • Nie używać trybu ECB do niczego
  • Nie pisać w PHP (opcjonalne)

Ale w sumie to nie o tym miało być.

Bonus: CTR mode

CTR mode

StackOverflow atakuje ponownie

StackOverflow atakuje ponownie

StackOverflow atakuje ponownie

StackOverflow atakuje ponownie

StackOverflow atakuje ponownie

StackOverflow atakuje ponownie

StackOverflow atakuje ponownie

Na szczęście

Na szczęście nikt nie

Na szczęście nikt nie kopiuje kodu

Na szczęście nikt nie kopiuje kodu bez myślenia

Na szczęście nikt nie kopiuje kodu bez myślenia, ...prawda?

:(

Tryb CBC

Tryby wiązania bloków

Mamy 16bajtowe bloki i 48bajtową wiadomość. Co robić?

Tryb ECB CBC

Tryb CBC

Tryb CBC

ct[0] = aes(pt[0] ^ iv)
ct[1] = aes(pt[1] ^ ct[0])
ct[2] = aes(pt[2] ^ ct[1])
...

Hello AbanteCart my old friend

function encrypt($str) {
    if (!$this->key) { return $str; }
    $enc_str = '';
    if (!$this->_check_openssl()) {
        //non openssl basic encryption
        for ($i = 0; $i < strlen($str); $i++){
            $char = substr($str, $i, 1);
            $keychar = substr($this->key, ($i % strlen($this->key)) - 1, 1);
            $enc_str .= chr(ord($char) + ord($keychar));
        }
        $enc_str = base64_encode($enc_str);
    } else {
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
        $enc_str = base64_encode(openssl_encrypt($str, 'aes-256-cbc', $this->key, 0, $iv) . '::' . $iv);
    }
    return str_replace('==', '', strtr($enc_str, '+/', '-_'));
}

Hello AbanteCart my old friend

function encrypt($str) {
    if (!$this->key) { return $str; }
    $enc_str = '';
    if (!$this->_check_openssl()) {
        //non openssl basic encryption
        for ($i = 0; $i < strlen($str); $i++){
            $char = substr($str, $i, 1);
            $keychar = substr($this->key, ($i % strlen($this->key)) - 1, 1);
            $enc_str .= chr(ord($char) + ord($keychar));
        }
        $enc_str = base64_encode($enc_str);
    } else {
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
        $enc_str = base64_encode(openssl_encrypt($str, 'aes-256-cbc', $this->key, 0, $iv) .
            '::' . $iv);
    }
    return str_replace('==', '', strtr($enc_str, '+/', '-_'));
}

Hello AbanteCart my old friend

function encrypt($str) {
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
    return base64_encode(openssl_encrypt($str, 'aes-256-cbc', $this->key, 0, $iv) .
        '::' . $iv);
}

CBC Mode decryption

CBC Mode decryption

IV:

61 73 39 64 38 37 39 38 39 33 68 38 77 65 73 66

Wiadomość zaszyfrowana AES-CBC:

81 32 30 85 2d ed 78 2d f0 5d 5b a1 c9 e9 b3 72

Oryginalna wiadomość:

m  s  m  &  a  d  m  i  n  =  0  05 05 05 05 05

Nie znamy klucza

CBC Mode decryption

IV:

60 73 39 64 38 37 39 38 39 33 68 38 77 65 73 66

Wiadomość zaszyfrowana AES-CBC:

81 32 30 85 2d ed 78 2d f0 5d 5b a1 c9 e9 b3 72

Zdeszyfrowana wiadomość:

l  s  m  &  a  d  m  i  n  =  0  05 05 05 05 05

Dalej nie znamy klucza

CBC Mode decryption

IV:

61 73 39 64 38 37 39 38 39 33 69 38 77 65 73 66

Wiadomość zaszyfrowana AES-CBC:

81 32 30 85 2d ed 78 2d f0 5d 5b a1 c9 e9 b3 72

Zdeszyfrowana wiadomość:

m  s  m  &  a  d  m  i  n  =  1  05 05 05 05 05

I ciągle nie znamy klucza

Wracając do AbanteCart

Kod logowania z AbanteCart:

$customer_data = $encryption->encrypt(serialize(array (
        'first_name'  => $this->firstname,
        'customer_id' => $this->customer_id,
        'script_name' => $this->request->server['SCRIPT_NAME']
)));
setcookie('customer', $customer_data /* ... */);

Wracając do AbanteCart

Przykładowy wynik:

a:3:{s:10:"first_name";s:6:"msm123";s:11:"customer_id";i:123;s:11:"script_name";N;}

Dla danych:

array(3) {
  ["first_name"]=> string(6) "msm123"
  ["customer_id"]=> int(123)
  ["script_name"]=> NULL
}

Wracając do AbanteCart

Zaszyfrowane - AES-CBC.

Możemy trywialnie wpłynąc na pierwsze 16 bajtów danych!

a:3:{s:10:"first_name";s:6:"msm123";s:11:"customer_id";i:123;s:11:"script_name";N;}

Trochę mało :(

a:3:{s:10:"first_name";s:6:"msm123";s:11:"customer_id";i:123;s:11:"script_name";N;}

Trochę mało?

Ale hm, możemy dowolnie nazwać użytkownika

a:3:{s:10:"first_name";s:6:"asdoj@:12939- /xys";s:11:"customer_id";i:123;s:11:"script_name";N;}

Wracając do AbanteCart

A co jakby nazwać swojego użytkownika tak?

;N;s:11:"customer_id";i:1;s:18:

Wracając do AbanteCart

Co nam to da?

Zaszyfrowane dane:

a:3:{s:10:"first_name";s:31:";N;s:11:"customer_id";i:1
;s:18:";s:11:"customer_id";i:123;s:11:"script_name";N;}

Wracając do AbanteCart

Zmieniając tylko dwa bajty...

a:3:{s:10:"first_name";s:31:";N;s:11:"customer_id";i:1
;s:18:";s:11:"customer_id";i:123;s:11:"script_name";N;}
a:4:{s:17:"first_name";s:31:";N;s:11:"customer_id";i:1
;s:18:";s:11:"customer_id";i:123;s:11:"script_name";N;}

Wracając do AbanteCart

Zmieniając tylko jeden bajt...

array(3) {
  ["first_name"]=>
  ";N;s:11:"customer_id";i:1;s:18:"
  ["customer_id"]=>
  int(123)
  ["script_name"]=>
  NULL
}

array(4) {
  ["first_name";s:31:"]=>
  NULL
  ["customer_id"]=>
  int(1)
  [";s:11:_customer_id"]=>
  int(123)
  ["script_name"]=>
  NULL
}

Zaorane

Wracając do AbanteCart

class ControllerPagesIndexForgotPassword extends AController {
    // ...
    public function main() {
        // ...
        if ($this->request->is_POST() && $this->_validate()) {
            //generate hash
            $hash = genToken(32);
            $enc = new AEncryption($this->config->get('encryption_key'));
            $rtoken = $enc->encrypt($this->request->post['username'].'::'.$hash);
            $link = $this->html->getSecureURL(
                'index/forgot_password/validate',
                '&rtoken='.$rtoken);
            // ...

Wracając do AbanteCart

class ControllerResponsesExtensionDefaultPaymate extends AController {
    // ...
    public function callback(){
        // ..
        if (isset($this->request->post['responseCode'])){
            if ($this->request->post['responseCode'] == 'PA' ||
                    $this->request->post['responseCode'] == 'PP'){
                if (isset($this->request->get['oid']) &&
                        isset($this->request->get['conf'])) {
                    $this->load->library('encryption');
                    $encryption = new AEncryption($this->config->get('encryption_key'));
                    $order_id = $encryption->decrypt($this->request->get['oid']);
                    if(!$order_id){ exit; }
                    $this->load->model('checkout/order');
                    // ...

Prawdziwy wniosek:

Szyfrowanie nie służy do uwierzytelniania

Prawdziwy wniosek:

Szyfrowanie nie służy do uwierzytelniania

Chyba że akurat służy

Prawdziwy wniosek:

Szyfrowanie nie służy do uwierzytelniania

Chyba że akurat służy     To wtedy ok

Prawdziwy wniosek:

Szyfrowanie nie służy do uwierzytelniania

Chyba że akurat służy     To wtedy ok

Patrz:
AES-OCB

AES-CCM

AES-GCM

Funkcje hashujące skrótu i podpisy

(kryptograficzne) Funkcje skrótu

Przykłady: MD5, SHA-1, SHA-2, SHA3

Funkcja skrótu to:

  • Jednokierunkowa funkcja jednego argumentu
  • Wejściem jest ciągiem bajtów o dowolnej długości
  • Wyjściem jest ciąg bajtów o stałej długości (np. 128 albo 256 bitów)

(kryptograficzne) Funkcje skrótu

Przykłady: MD5, SHA-1, SHA-2, SHA3

Funkcja skrótu to:

  • Jednokierunkowa funkcja jednego argumentu
  • Wejściem jest ciągiem bajtów o dowolnej długości
  • Wyjściem jest ciąg bajtów o stałej długości (np. 128 albo 256 bitów)

(kryptograficzne) Funkcje skrótu

Przykłady: MD5, SHA-1, SHA-2, SHA-3

Funkcja skrótu to:

  • Jednokierunkowa funkcja jednego argumentu
  • Wejściem jest ciągiem bajtów o dowolnej długości
  • Wyjściem jest ciąg bajtów o stałej długości (np. 128 albo 256 bitów)

(kryptograficzne) Funkcje skrótu

Przykłady: MD5, SHA-1, SHA-2 (SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256), SHA-3

Funkcja skrótu to:

  • Jednokierunkowa funkcja jednego argumentu
  • Wejściem jest ciągiem bajtów o dowolnej długości
  • Wyjściem jest ciąg bajtów o stałej długości (np. 128 albo 256 bitów)

(kryptograficzne) Funkcje skrótu

Przykłady: MD5, SHA-1, SHA-2 (SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256), SHA-3 (SHA3-224, SHA3-256, SHA3-384, SHA3-512)

Funkcja skrótu to:

  • Jednokierunkowa funkcja jednego argumentu
  • Wejściem jest ciągiem bajtów o dowolnej długości
  • Wyjściem jest ciąg bajtów o stałej długości (np. 128 albo 256 bitów)

(kryptograficzne) Funkcje skrótu

Przykłady: MD5, SHA-1, SHA-2 (SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256), SHA-3 = Keccak (SHA3-224, SHA3-256, SHA3-384, SHA3-512 (SHAKE128, SHAKE256, KangooroTwelve))

Funkcja skrótu to:

  • Jednokierunkowa funkcja jednego argumentu
  • Wejściem jest ciągiem bajtów o dowolnej długości
  • Wyjściem jest ciąg bajtów o stałej długości (np. 128 albo 256 bitów)

(kryptograficzne) Funkcje skrótu

Własności:

  • Pre-image resistance: mając hash ciężko znaleźć pasującą wiadomość.
  • Collision resistance: ciężko znaleźć dwie dowolne wiadomości mające ten sam hash.

(kryptograficzne) Funkcje skrótu

Własności:

  • Pre-image resistance: mając hash ciężko znaleźć pasującą wiadomość.
  • Collision resistance: ciężko znaleźć dwie dowolne wiadomości mające ten sam hash.

Nie ma tu nic o uwierzytelnianiu

Hashowanie to tez nie uwierzytelnianie

To nie jest podpisywanie wiadomości:

import hashlib
SECRET = '53e49e1d8cce03c52f24bd472169c1f7'.decode('hex')

def sign(message):
    return hashlib.sha256(SECRET + message).hexdigest()

Hashowanie to tez nie uwierzytelnianie

To jest podpisywanie wiadomości:

import hashlib
SECRET = '53e49e1d8cce03c52f24bd472169c1f7'.decode('hex')

def sign(message):
    return hashlib.hmac(message, SECRET, hashlib.sha256).hexdigest()

Dlaczego nie?

Rabat na jabłka!

name=rabat_na_jablka&amount=10

Generowanie podpisu:

>>> hashlib.md5('pH2ef4OhUdec3eSPdrujAy4gdec3eSPdrudec3eSPdru' +
                ':name=rabat_na_jablka&amount=10').hexdigest()
'8b990fdf27e79353a7fadf639e5fa7c4'

Pompowanie haszy

Pobieramy https://github.com/bwall/HashPump

sudo apt install hashpump

Atakujemy:

$ hashpump -d ':name=rabat_na_jablka&amount=10' -s '8b990fdf27e79353a7fadf639e5fa7c4'
            -k 44 -a '&amount=100'
ef7ca4ca05a171525a668d23ac6c639a
:name=rabat_na_jablka&amount=10\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x02\x00\x00\x00\x00\x00\x00&amount=100

Pompowanie haszy

Nowa wiadomość:

:name=rabat_na_jablka&amount=10\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x02\x00\x00\x00\x00\x00\x00&amount=100

Nowy hash:

ef7ca4ca05a171525a668d23ac6c639a

Haszowanie to nie uwierzytelnienie

Czas wykonania ataku: ~~minuta

5 minut łącznie z pobieraniem

>>> hashlib.md5('pH2ef4OhUdec3eSPdrujAy4gdec3eSPdrudec3eSPdru:name=rabat_na_jablka&
amount=10\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00X\x02\x00\x00\x00\x00\x00\x00&amount=100').hexdigest()
'ef7ca4ca05a171525a668d23ac6c639a'

Case study: MasterCard

Case study: MasterCard

Schemat "podpisywania danych":

vpc_Amount=10000
vpc_Card=1234567890123456
vpc_Name=Joe

Dane są sklejane:

100001234567890123456Joe

I "podpisywane":

md5(secret + data)

Case study: MasterCard

Schemat "podpisywania danych":

vpc_Amount=1
vpc_B=0000
vpc_Card=1234567890123456
vpc_Name=Joe

Dane są sklejane:

100001234567890123456Joe

I "podpisywane":

md5(secret + data)

Case study: MasterCard

Schemat "podpisywania danych":
vpc_Amount=10000
vpc_Card=1234567890123456
vpc_Name=Joe
Dane są sklejane:
100001234567890123456Joe
I "podpisywane":
md5(secret + data)
Schemat "podpisywania danych":
vpc_Amount=1
vpc_B=0000
vpc_Card=1234567890123456
vpc_Name=Joe
Dane są sklejane:
100001234567890123456Joe
I "podpisywane":
md5(secret + data)

Case study: `____ _____`

  • Rok 2015
  • Polska bramka płatności
  • Bardzo podobny błąd

Case study: DotPay

Case study: Przelewy 24

16 znaków 0-9a-f

Czyli 8 bajtów

Czyli 64 bity

Hmm...

Własna kryptografia

Własna kryptografia

Zagadka!

Jeśli użycie gotowych prymitywów kryptograficznych jest trudne...

To stworzenie własnych prymitywów kryptograficznych od zera i użycie ich jest ...?

Własna kryptografia

Zagadka!

Jeśli użycie gotowych prymitywów kryptograficznych jest trudne...

To stworzenie własnych prymitywów kryptograficznych od zera i użycie ich jest bardzo trudne

Czasy życia funkcji skrótu

Security BSides 2017

Security BSides 2017

Security BSides 2017

StreamHash4

StreamHash4

StreamHash4

Stan: s0, s1, s2, s3 (startowo c0, c1, c2, c3)

Funkcja rundy:

def update(state, data):
    s0, s1, s2, s3 = state
    s0 = aes_round(s0 ^ data, c0)
    s1 = aes_round(s1 ^ data, c1)
    s2 = aes_round(s2 ^ data, c2)
    s3 = aes_round(s3 ^ data, c3)  # (simplified version)
    return s0, s1, s2, s3

Na koniec:

result = finalize(state)

StreamHash4

Czyli szyfrowanie wiadomości z czterema blokami:

def streamhash4(msg):
    # msg = a0 + a1 + a2 + a3
    msg = '.................'
    a0, a1, a2, a3 = chunks(msg, 16)
    result = (c0, c1, c2, c3)
    result = update(result, a1)
    result = update(result, a2)
    result = update(result, a3)
    result = update(result, a4)
    return finalize(result)

Uwaga, matematyka!

Intuicyjny Warunek kolizji

streamhash4(A) = streamhash4(B)

Ofc zakładamy że A != B, bo inaczej problem byłby trywialny

Intuicyjny Warunek kolizji

streamhash4(A) = streamhash4(B)

Rozwijając:

a0, a1, a2, a3 = A
b0, b1, b3, b4 = B
C = c0, c1, c2, c3
update(update(update(update(C, a0), a1), a2), a3) 
    = update(update(update(update(C, b0), b1), b2), b3)

Trocję mniej intuicyjny warunek

  aes(aes(aes(aes(a0 ^ c0, c0) ^ c0, c0) ^ a2, c0) ^ a3, c0) 
= aes(aes(aes(aes(b0 ^ c0, c0) ^ c0, c0) ^ b2, c0) ^ b3, c0)

  aes(aes(aes(aes(a0 ^ c1, c1) ^ c1, c1) ^ a2, c1) ^ a3, c1)
= aes(aes(aes(aes(b0 ^ c1, c1) ^ c1, c1) ^ b2, c1) ^ b3, c1)

  aes(aes(aes(aes(a0 ^ c2, c2) ^ c2, c2) ^ a2, c2) ^ a3, c2)
= aes(aes(aes(aes(b0 ^ c2, c2) ^ c2, c2) ^ b2, c2) ^ b3, c2)

  aes(aes(aes(aes(a0 ^ c3, c3) ^ c3, c3) ^ a2, c3) ^ a3, c3)
= aes(aes(aes(aes(b0 ^ c3, c3) ^ c3, c3) ^ b2, c3) ^ b3, c3)

Warunek kolizji

A00 = aes(a0 ^ c0, c0)    B00 = aes(b0 ^ c0, c0)
A01 = aes(a0 ^ c1, c1)    B01 = aes(b0 ^ c1, c1)
A02 = aes(a0 ^ c2, c2)    B02 = aes(b0 ^ c2, c2)
A03 = aes(a0 ^ c3, c3)    B03 = aes(b0 ^ c3, c3)

Warunek kolizji:

aes(aes(aes(A00 ^ c0, c0) ^ a2, c0) ^ a3, c0) = aes(aes(aes(B00 ^ c0, c0) ^ b2, c0) ^ b3, c0)
aes(aes(aes(A01 ^ c1, c1) ^ a2, c1) ^ a3, c1) = aes(aes(aes(B01 ^ c1, c1) ^ b2, c1) ^ b3, c1)
aes(aes(aes(A02 ^ c2, c2) ^ a2, c2) ^ a3, c2) = aes(aes(aes(B02 ^ c2, c2) ^ b2, c2) ^ b3, c2)
aes(aes(aes(A03 ^ c3, c3) ^ a2, c3) ^ a3, c3) = aes(aes(aes(B03 ^ c3, c3) ^ b2, c3) ^ b3, c3)

Warunek kolizji

A10 = aes(A00 ^ c0, c0)    B10 = aes(B00 ^ c0, c0)
A11 = aes(A01 ^ c1, c1)    B11 = aes(B01 ^ c1, c1)
A12 = aes(A02 ^ c2, c2)    B12 = aes(B02 ^ c2, c2)
A13 = aes(A03 ^ c3, c3)    B13 = aes(B03 ^ c3, c3)

Warunek kolizji:

aes(aes(A10 ^ a2, c0) ^ a3, c0) = aes(aes(B10 ^ b2, c0) ^ b3, c0)
aes(aes(A11 ^ a2, c1) ^ a3, c1) = aes(aes(B11 ^ b2, c1) ^ b3, c1)
aes(aes(A12 ^ a2, c2) ^ a3, c2) = aes(aes(B12 ^ b2, c2) ^ b3, c2)
aes(aes(A13 ^ a2, c3) ^ a3, c3) = aes(aes(B13 ^ b2, c3) ^ b3, c3)

Atakujemy!

Przed:

aes(aes(A10 ^ a2, c0) ^ a3, c0) = aes(aes(B10 ^ b2, c0) ^ b3, c0)
aes(aes(A11 ^ a2, c1) ^ a3, c1) = aes(aes(B11 ^ b2, c1) ^ b3, c1)
aes(aes(A12 ^ a2, c2) ^ a3, c2) = aes(aes(B12 ^ b2, c2) ^ b3, c2)
aes(aes(A13 ^ a2, c3) ^ a3, c3) = aes(aes(B13 ^ b2, c3) ^ b3, c3)

Po:

aes(A10 ^ a2, c0) ^ a3 = aes(B10 ^ b2, c0) ^ b3
aes(A11 ^ a2, c1) ^ a3 = aes(B11 ^ b2, c1) ^ b3
aes(A12 ^ a2, c2) ^ a3 = aes(B12 ^ b2, c2) ^ b3
aes(A13 ^ a2, c3) ^ a3 = aes(B13 ^ b2, c3) ^ b3

Atakujemy!

Przed:

aes(A10 ^ a2, c0) ^ a3 = aes(B10 ^ b2, c0) ^ b3
aes(A11 ^ a2, c1) ^ a3 = aes(B11 ^ b2, c1) ^ b3
aes(A12 ^ a2, c2) ^ a3 = aes(B12 ^ b2, c2) ^ b3
aes(A13 ^ a2, c3) ^ a3 = aes(B13 ^ b2, c3) ^ b3

Po:

aes(A10 ^ a2, c0) = aes(B10 ^ b2, c0) ^ b3 ^ a3
aes(A11 ^ a2, c1) = aes(B11 ^ b2, c1) ^ b3 ^ a3
aes(A12 ^ a2, c2) = aes(B12 ^ b2, c2) ^ b3 ^ a3
aes(A13 ^ a2, c3) = aes(B13 ^ b2, c3) ^ b3 ^ a3

Atakujemy!

Przed:

aes(A10 ^ a2, c0) = aes(B10 ^ b2, c0) ^ (b3 ^ a3)
aes(A11 ^ a2, c1) = aes(B11 ^ b2, c1) ^ (b3 ^ a3)
aes(A12 ^ a2, c2) = aes(B12 ^ b2, c2) ^ (b3 ^ a3)
aes(A13 ^ a2, c3) = aes(B13 ^ b2, c3) ^ (b3 ^ a3)

Po:

ar(mx(sr(sb(A10 ^ a2))), c0) = ar(mx(sr(sb(B10 ^ b2))), c0) ^ (b3 ^ a3)
ar(mx(sr(sb(A11 ^ a2))), c1) = ar(mx(sr(sb(B11 ^ b2))), c1) ^ (b3 ^ a3)
ar(mx(sr(sb(A12 ^ a2))), c2) = ar(mx(sr(sb(B12 ^ b2))), c2) ^ (b3 ^ a3)
ar(mx(sr(sb(A13 ^ a2))), c3) = ar(mx(sr(sb(B13 ^ b2))), c3) ^ (b3 ^ a3)

Atakujemy!

Przed:

ar(mx(sr(sb(A10 ^ a2))), c0) = ar(mx(sr(sb(B10 ^ b2))), c0) ^ (b3 ^ a3)
ar(mx(sr(sb(A11 ^ a2))), c1) = ar(mx(sr(sb(B11 ^ b2))), c1) ^ (b3 ^ a3)
ar(mx(sr(sb(A12 ^ a2))), c2) = ar(mx(sr(sb(B12 ^ b2))), c2) ^ (b3 ^ a3)
ar(mx(sr(sb(A13 ^ a2))), c3) = ar(mx(sr(sb(B13 ^ b2))), c3) ^ (b3 ^ a3)

Po:

mx(sr(sb(A10 ^ a2))) ^ c0 = mx(sr(sb(B10 ^ b2))) ^ c0 ^ (b3 ^ a3)
mx(sr(sb(A11 ^ a2))) ^ c1 = mx(sr(sb(B11 ^ b2))) ^ c1 ^ (b3 ^ a3)
mx(sr(sb(A12 ^ a2))) ^ c2 = mx(sr(sb(B12 ^ b2))) ^ c2 ^ (b3 ^ a3)
mx(sr(sb(A13 ^ a2))) ^ c3 = mx(sr(sb(B13 ^ b2))) ^ c3 ^ (b3 ^ a3)

Atakujemy!

Przed:

mx(sr(sb(A10 ^ a2))) ^ c0 = mx(sr(sb(B10 ^ b2))) ^ c0 ^ (b3 ^ a3)
mx(sr(sb(A11 ^ a2))) ^ c1 = mx(sr(sb(B11 ^ b2))) ^ c1 ^ (b3 ^ a3)
mx(sr(sb(A12 ^ a2))) ^ c2 = mx(sr(sb(B12 ^ b2))) ^ c2 ^ (b3 ^ a3)
mx(sr(sb(A13 ^ a2))) ^ c3 = mx(sr(sb(B13 ^ b2))) ^ c3 ^ (b3 ^ a3)

Po:

mx(sr(sb(A10 ^ a2))) = mx(sr(sb(B10 ^ b2))) ^ (b3 ^ a3)
mx(sr(sb(A11 ^ a2))) = mx(sr(sb(B11 ^ b2))) ^ (b3 ^ a3)
mx(sr(sb(A12 ^ a2))) = mx(sr(sb(B12 ^ b2))) ^ (b3 ^ a3)
mx(sr(sb(A13 ^ a2))) = mx(sr(sb(B13 ^ b2))) ^ (b3 ^ a3)

Atakujemy!

Przed:

mx(sr(sb(A10 ^ a2))) = mx(sr(sb(B10 ^ b2))) ^ (b3 ^ a3)
mx(sr(sb(A11 ^ a2))) = mx(sr(sb(B11 ^ b2))) ^ (b3 ^ a3)
mx(sr(sb(A12 ^ a2))) = mx(sr(sb(B12 ^ b2))) ^ (b3 ^ a3)
mx(sr(sb(A13 ^ a2))) = mx(sr(sb(B13 ^ b2))) ^ (b3 ^ a3)

Po:

mx(sr(sb(A10 ^ a2))) = mx(sr(sb(B10 ^ b2)) ^ invmx(b3 ^ a3))
mx(sr(sb(A11 ^ a2))) = mx(sr(sb(B11 ^ b2)) ^ invmx(b3 ^ a3))
mx(sr(sb(A12 ^ a2))) = mx(sr(sb(B12 ^ b2)) ^ invmx(b3 ^ a3))
mx(sr(sb(A13 ^ a2))) = mx(sr(sb(B13 ^ b2)) ^ invmx(b3 ^ a3))

mx(A) ^ B = mx(A ^ invmx(B))

Atakujemy!

Przed:

mx(sr(sb(A10 ^ a2))) = mx(sr(sb(B10 ^ b2))) ^ invmx(b3 ^ a3))
mx(sr(sb(A11 ^ a2))) = mx(sr(sb(B11 ^ b2))) ^ invmx(b3 ^ a3))
mx(sr(sb(A12 ^ a2))) = mx(sr(sb(B12 ^ b2))) ^ invmx(b3 ^ a3))
mx(sr(sb(A13 ^ a2))) = mx(sr(sb(B13 ^ b2))) ^ invmx(b3 ^ a3))

Po:

sr(sb(A10 ^ a2)) = sr(sb(B10 ^ b2)) ^ invmx(b3 ^ a3)
sr(sb(A11 ^ a2)) = sr(sb(B11 ^ b2)) ^ invmx(b3 ^ a3)
sr(sb(A12 ^ a2)) = sr(sb(B12 ^ b2)) ^ invmx(b3 ^ a3)
sr(sb(A13 ^ a2)) = sr(sb(B13 ^ b2)) ^ invmx(b3 ^ a3)

mx(X) = mx(Y)  <=> X = Y

Atakujemy!

Przed:

sr(sb(A10 ^ a2)) = sr(sb(B10 ^ b2)) ^ invmx(b3 ^ a3)
sr(sb(A11 ^ a2)) = sr(sb(B11 ^ b2)) ^ invmx(b3 ^ a3)
sr(sb(A12 ^ a2)) = sr(sb(B12 ^ b2)) ^ invmx(b3 ^ a3)
sr(sb(A13 ^ a2)) = sr(sb(B13 ^ b2)) ^ invmx(b3 ^ a3)

Po:

sr(sb(A10 ^ a2)) = sr(sb(B10 ^ b2) ^ invsr(invmx(b3 ^ a3)))
sr(sb(A11 ^ a2)) = sr(sb(B11 ^ b2) ^ invsr(invmx(b3 ^ a3)))
sr(sb(A12 ^ a2)) = sr(sb(B12 ^ b2) ^ invsr(invmx(b3 ^ a3)))
sr(sb(A13 ^ a2)) = sr(sb(B13 ^ b2) ^ invsr(invmx(b3 ^ a3)))

sr(A) ^ B = sr(A ^ invsr(B))

Atakujemy!

Przed:

sr(sb(A10 ^ a2)) = sr(sb(B10 ^ b2) ^ invsr(invmx(b3 ^ a3)))
sr(sb(A11 ^ a2)) = sr(sb(B11 ^ b2) ^ invsr(invmx(b3 ^ a3)))
sr(sb(A12 ^ a2)) = sr(sb(B12 ^ b2) ^ invsr(invmx(b3 ^ a3)))
sr(sb(A13 ^ a2)) = sr(sb(B13 ^ b2) ^ invsr(invmx(b3 ^ a3)))

Po:

sb(A10 ^ a2) = sb(B10 ^ b2) ^ invsr(invmx(b3 ^ a3))
sb(A11 ^ a2) = sb(B11 ^ b2) ^ invsr(invmx(b3 ^ a3))
sb(A12 ^ a2) = sb(B12 ^ b2) ^ invsr(invmx(b3 ^ a3))
sb(A13 ^ a2) = sb(B13 ^ b2) ^ invsr(invmx(b3 ^ a3))

sr(X) = sr(Y)  <=> X = Y

Atakujemy!

Przed:

sb(A10 ^ a2) = sb(B10 ^ b2) ^ invsr(invmx(b3 ^ a3))
sb(A11 ^ a2) = sb(B11 ^ b2) ^ invsr(invmx(b3 ^ a3))
sb(A12 ^ a2) = sb(B12 ^ b2) ^ invsr(invmx(b3 ^ a3))
sb(A13 ^ a2) = sb(B13 ^ b2) ^ invsr(invmx(b3 ^ a3))

Po:

invsr(invmx(b3 ^ a3)) = sb(B10 ^ b2) ^ sb(A10 ^ a2)
invsr(invmx(b3 ^ a3)) = sb(B11 ^ b2) ^ sb(A11 ^ a2)
invsr(invmx(b3 ^ a3)) = sb(B12 ^ b2) ^ sb(A12 ^ a2)
invsr(invmx(b3 ^ a3)) = sb(B13 ^ b2) ^ sb(A13 ^ a2)

Atakujemy!

Przed:

invsr(invmx(b3 ^ a3)) = sb(B10 ^ b2) ^ sb(A10 ^ a2)
invsr(invmx(b3 ^ a3)) = sb(B11 ^ b2) ^ sb(A11 ^ a2)
invsr(invmx(b3 ^ a3)) = sb(B12 ^ b2) ^ sb(A12 ^ a2)
invsr(invmx(b3 ^ a3)) = sb(B13 ^ b2) ^ sb(A13 ^ a2)

Po:

  sb(B10 ^ b2) ^ sb(A10 ^ a2)
= sb(B11 ^ b2) ^ sb(A11 ^ a2)
= sb(B12 ^ b2) ^ sb(A12 ^ a2)
= sb(B13 ^ b2) ^ sb(A13 ^ a2)

Lepiej!

sb(B10^b2)^sb(A10^a2) = sb(B11^b2)^sb(A11^a2) = sb(B12^b2)^sb(A12^a2) = sb(B13^b2)^sb(A13^a2)

Przypomnienie: jeśli będziemy w stanie rozwiązać to równanie, to (odwracając wszystkie kroki) dostaniemy kolizję hasha.

Lepiej!

sb(B10^b2)^sb(A10^a2) = sb(B11^b2)^sb(A11^a2) = sb(B12^b2)^sb(A12^a2) = sb(B13^b2)^sb(A13^a2)

sb(x) = Substitutions[x]

Substitutions[256] = { 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, (...) 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, (...) 0xC0, 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, (...) 0x15, 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, (...) 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, (...) 0x84, 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, (...) 0xCF, 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, (...) 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, (...) 0xD2, 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, (...) 0x73, (...) (...) (...) (...) (...) (...) (...) (...) (...) (...) (...) (...) 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, (...) 0x16 };

Lepiej!

sb(B10 ^ b2) ^ sb(A10 ^ a2)
wynik[0] = sb(B10[0] ^ b2[0]) ^ sb(A10[0] ^ a2[0])
wynik[1] = sb(B10[1] ^ b2[1]) ^ sb(A10[1] ^ a2[1])
wynik[2] = sb(B10[2] ^ b2[2]) ^ sb(A10[2] ^ a2[2])
...

Lepiej!

sb(B10 ^ b2) ^ sb(A10 ^ a2)
wynik[0] = sb(B10[0] ^ b2[0]) ^ sb(A10[0] ^ a2[0])
wynik[1] = sb(B10[1] ^ b2[1]) ^ sb(A10[1] ^ a2[1])
wynik[2] = sb(B10[2] ^ b2[2]) ^ sb(A10[2] ^ a2[2])
...
Wniosek: można zgadywać rozwiązanie równania "bajt po bajcie" - co jest bardzo szybkie.

Pojedynczo, bajt po bajcie...

 sb(B10 ^ b2)[ 0] ^ sb(A10 ^ a2)[ 0]
= sb(B11 ^ b2)[ 0] ^ sb(A11 ^ a2)[ 0]
= sb(B12 ^ b2)[ 0] ^ sb(A12 ^ a2)[ 0]
= sb(B13 ^ b2)[ 0] ^ sb(A13 ^ a2)[ 0]

Pojedynczo, bajt po bajcie...

 sb(B10 ^ b2)[ 0] ^ sb(A10 ^ a2)[ 0]
= sb(B11 ^ b2)[ 0] ^ sb(A11 ^ a2)[ 0]
= sb(B12 ^ b2)[ 0] ^ sb(A12 ^ a2)[ 0]
= sb(B13 ^ b2)[ 0] ^ sb(A13 ^ a2)[ 0]

Atak: brute-forcujemy bajt po bajcie b2 aż równanie będzie spełnione.

Złożoność: 2**8

Pojedynczo, bajt po bajcie...

sb(B10 ^ b2)[ 0] ^ sb(A10 ^ a2)[ 0]
= sb(B11 ^ b2)[ 0] ^ sb(A11 ^ a2)[ 0]
= sb(B12 ^ b2)[ 0] ^ sb(A12 ^ a2)[ 0]
= sb(B13 ^ b2)[ 0] ^ sb(A13 ^ a2)[ 0]

Atak: brute-forcujemy bajt po bajcie b2 i a2 aż równanie będzie spełnione.

Złożoność: 2**(8+8) = 2**16

Pojedynczo, bajt po bajcie...

for i in range(16):
    B00i, B01i, B02i = ord(B00[i]), ord(B01[i]), ord(B02[i])
    A00i, A01i, A02i = ord(A00[i]), ord(A01[i]), ord(A02[i])
    for a1 in range(256):
        for b1 in range(256):
            AB2_0 = aes.Sbox[A00i ^ a1] ^ aes.Sbox[B00i ^ b1]
            AB2_1 = aes.Sbox[A01i ^ a1] ^ aes.Sbox[B01i ^ b1]
            AB2_2 = aes.Sbox[A02i ^ a1] ^ aes.Sbox[B02i ^ b1]
            if AB2_0 == AB2_1 == AB2_2:
                a1_out[i] = a1
                b1_out[i] = b1
                break

Pojedynczo, bajt po bajcie...

     sb(B10 ^ b2)[ 0] ^ sb(A10 ^ a2)[ 0]
   = sb(B11 ^ b2)[ 0] ^ sb(A11 ^ a2)[ 0]
   = sb(B12 ^ b2)[ 0] ^ sb(A12 ^ a2)[ 0]
   = sb(B13 ^ b2)[ 0] ^ sb(A13 ^ a2)[ 0]

???

Pojedynczo, bajt po bajcie...

     sb(B10 ^ b2)[ 0] ^ sb(A10 ^ a2)[ 0]
   = sb(B11 ^ b2)[ 0] ^ sb(A11 ^ a2)[ 0]
   = sb(B12 ^ b2)[ 0] ^ sb(A12 ^ a2)[ 0]
   = sb(B13 ^ b2)[ 0] ^ sb(A13 ^ a2)[ 0]

Atak: brute-forcujemy jeden bajt z a2 i b2, oraz dodatkowo cztery bajty z a2 aż równanie będzie spełnione.

Złożoność: 2**(8+8+32) = 2**48

Pojedynczo, bajt po bajcie...

bool brute_row(int seg) {
    char a0_inv[16];
    for (uint64_t mod = 0x0; mod < 256LL*256*256*256; mod++) {

        *(uint32_t*)&a0[seg*4] = mod;
        inv_shift_rows(a0_inv, a0);
        for (int i = seg*4; i < seg*4+4; i++) {
            struct find_result res = find(*(__m128i*)a0_inv, i);
            if (res.a1 >= 0 && res.b1 >= 0) {
                if (i >= seg*4+3) {
                    hexdump(a0);
                    return true;
                }
            } else { break; }
        }
    }
    return false;
}

Kolizje!

collision.1.bin

4d6f676c6173206d6f6a61206279632c
fafb16f8b77339b985d5c24936415776
df044e598f03deb2204f77d9e603e63c
00000000000000000000000000000000

collision.2.bin

4b72797a79736f7761206e61727a6563
00000000000000000000000000000000
c09724a230bad9348bf38216bfc2f6f6
de9ba28a01f0673fe6524ef7d122bb2d

Screen dla niewiernych

:<

Własna kryptografia

Zagadka!

Jeśli nawet algorytmy stworzone przez najlepszych kryptografów są łamane... (md5, sha1, streamhash)

To szansa że my, prości programiści, wymyślimy coś naprawdę bezpiecznego jest zerowa

(PS. Prościej atakować niż bronić)

Epilog?

Więcej materiałów

/join #cert.pl?

/join #cert.pl?

  • Tak, rekrutujemy
  • Nie trzeba mieć doświadczenia w temacie
  • Chociaż fajnie odróżniać DDoS od phishingu albo internet od przeglądarki
  • Można robić ciekawe rzeczy, jak ktoś chce

Q & A

msm msm@tailcall.net @MsmCode