25. maaliskuuta 2008

Sampo Pankin epäturvallinen nettipankki vakoilee käyttäjiään

Vihdoin ja viimein Sampo Pankki sai nettipankkinsa toimimaan, sen oltua koko pääsiäisen ajan suljettuna. Koko sotkun syynä on se, että Sampo Pankki on nyt yhdistynyt Danske Banken kanssa ja asiakasrekisterit ja pankkitoiminnot sun muut pitää saattaa ajantasalle. Samalla on luvattu parantaa nettipankin tietoturvaa viimeaikaistenkin nettipankkihyökkäysten (esim1, esim2) vuoksi luultavasti.

No mitä oli seurauksena?

Sampo Pankin nettipankissa on cross-site scripting haavoittuvuus. Tämä avaa uusia hyökkäysmahdollisuuksia esimerkiksi tietojen, kuten pankkitunnusten ja turvalukujen, kalasteluun. Sivu, jolle asiakas menee, vaikuttaa aivan selvästi olevan Sampo Pankin nettisivu, vaikka todellisuudessa se voi olla mikä tahansa sivu netissä. Nettipankin käyttäjä voi helposti täyttää kirjautumistietojaan siis hakkerin sivulle ja avata nettipankkinsa ties kenelle.





Tämän vakavan tietoturva-aukon lisäksi kyseinen nettipankki käyttää outoa java-viritystä, joka ilmeisesti urkkii käyttäjän tietokoneesta tietoja ja lähettää niitä pankille! Pankille lähtevät tiedot käyttäjän mm. BIOSista, näppäimistön ja äänikortin merkistä, prosessorityypistä jne. Saman rajapinnan kautta, jota Sampo Pankin nettipankki käyttää, olisi myös mahdollista lähettää pankille tietoja mm. työaseman ja toimialueen nimestä. Miksi ja mihin näitä tietoja kerätään? Miksei asiakkaille selkeästi kerrota tästä asiasta?

Tietoturvaviritys ihmetyttää myös. Ainakaan kirjautumisen suhteen ei ole mitään olennaista parannusta tullut, sisään pääsee yhä kirjautumaan (puolitiehen vain tosin) käyttäjänumerolla ja 4-numeroisella salasanalla! Tämän jälkeen järjestelmä kysyy vasta kertakäyttöisen avainluvun, mutta senkin kertomalla, mikä avainluku kortilta pitää lukea! Vertaa tätä vaikkapa Nordean verkkopankkiin, johon pääsee sisään vain osaamalla piiloitetun käyttäjänumeron ja kertakäyttöisen avainluvun (jonka järjestysnumeroa ei kerrota!). Ei hyvältä vaikuta Sampo Pankin viritys.

Lisäksi, kun Sampo Pankin nettipankkiin on päässyt sisälle, voi vaihtaa omia yhteystietojaan, eikä tässä vaiheessa kysytä mitään turvalukuja. Näin ollen, jos pahantekijä pääsee koneelle ollessasi siellä pankkitunnuksilla kirjautuneena (tai arvaten vaikkapa tuon kertakäyttöisen luvun), hän voi muuttaa yhteystietojasi ja tilata uudet tunnukset kätevästi postitse! On käsittämätöntä, että niinkin tärkeä asia kuin asiakkaan yhteystietojen muuttaminen voi onnistua Sampo Pankissa ilman turvalukua.

Yhteys tietokoneeltasi Sampo Pankin nettipankkiin on salattu SSL/TLS tekniikalla. Mutta avainkokojen puolesta en menisi sanomaan järjestelmää mitenkään "huipputurvalliseksi". Kuten kuvasta näkyy, salaus on 128bit RC4 ja 1024bit RSA (jo murretulla SHA-1 tiivistefunktiolla). Sampo Pankin käyttämässä RC4:ssä ja sen 128bit avainkoossa ei ole riittävää turvamarginaalia ja 1024bit RSA ei anna kuin n. "80bittiä suojaa", eikä sen käyttöä muutenkaan enää suositella salauksen heikkouden vuoksi. SHA-1 ei ole SSL/TLS:ssä vaihtoehtoa valitettavasti, joten tässä tapauksessa se voi vaarantaa tietoliikenteen ja avaimet manipuloinnille jne. Mielestäni salauksen tulisi ehdottomasti olla AES-256 ja 4096bit RSA/Diffie-Hellmann. Tai edes 3DES ja 2048bit RSA, jotta salauksen vahvuuteen voisi luottaa.


Sampo Pankin sivuilla olevat tietoturvaohjeet ovat puolestaan varsin suppeat, mutta sisällöltään toki ihan asialliset. Parannettavaa kuitenkin on selvästi Sampo Pankilla, katso vaikka vertailun vuoksi Nordean tietoturvaohjeet.

Miten nettipankki sitten pitäisi käyttäjää ajatellen turvata järkevästi? No, paras ratkaisu lienisi käyttää älykortinlukijaa ja älykorttia yhdessä paperilla olevien kertakäyttöisten turvalukujen kanssa. Nettiyhteys tulisi ehdottomasti olla vahvemmin kryptattu ja käyttäjille pitäisi olla kattavammat ohjeet tietokoneensa suojaamiseen ja etenkin kaikenlaisten huijausviestien varalle tulisi antaa riittävästi varoitusta. Pankin asiakkailleen sponsoroima hyvä virustentorjuntaohjelmisto olisi tietysti plussaa. :-)

Kun kokeilin säätää nettipankin asetuksia, tuli lisäksi tuollainen kryptinen virheilmoitus... Heh.
Sampo Pankin tietoturva vaikuttaa todella huonolta. Mistähän hyvästä tuonkin kehittäjille on palkkaa maksettu...?

Kaikesta huolimatta on syytä muistaa, että suurin tietoturvaongelma on käyttäjä itse. Käyttäjä, joka ei päivitä tietokonettaan, ei pidä ajantasaista tehokasta virustentorjuntaa, suorittaa kaikenlaisia ohjelmia ja tiedostoja joita ei varmuudella tiedä turvalliseksi, sekä antaa tunnuksensa miltei kenelle tahansa niitä kyselevälle, en edelleenkin se heikoin lenkki turvallisuuden ketjussa. Mutta, kaikkea ei voi laittaa käyttäjän viaksi eikä vastuulle. Pankilla tulee myöskin olla velvollisuutensa huolehtia nettipankkinsa tietoturvasta eikä vierittää vastuuta 100% kuluttajan niskaan.

7 kommenttia:

Timo kirjoitti...

No nyt sitten on cross-site skriptauskin mahdollista: http://www.digitoday.fi/tietoturva/2008/03/26/Sampo+pankin+sivut+avoinna+kalastelijoille/20088576/66

Markus Jansson kirjoitti...

Joo, huomasin just saman itsekin. Aika säälittävää toimintaa Sampo Pankilta.

Pitääpä hakea jostain IRC:stä noita skriptipätkiä ja kokeilla itsekin mitä kaikkea hauskaa niillä saakaan aikaiseksi. Heh. Tietysti jos jollakin on jo valmiina, niin postatkaapa tänne vaan tai pistäkää sähköpostitse mulle. ;)

Anonyymi kirjoitti...

package dk.danskebank.ec.ec.esafekey.businesslogic;

import dk.danskebank.ec.ec.esafekey.utility.Debug;
import dk.danskebank.ec.ec.esafekey.utility.Utility;
import java.io.FileInputStream;
import java.io.FileOutputStream;

// Referenced classes of package dk.danskebank.ec.ec.esafekey.businesslogic:
// CryptoKernel, JNILoader

public class IDFactory
{

public IDFactory()
throws Exception
{
try
{
isLoaded();
return;
}
catch(UnsatisfiedLinkError _ex)
{
JNILoader.loadJNI(1, true);
}
}

public String getIDs()
{
String s;
byte abyte0[];
try
{
FileInputStream fileinputstream;
abyte0 = new byte[(fileinputstream = new FileInputStream(Utility.getComponentHome() + "global.dat")).available()];
fileinputstream.read(abyte0);
fileinputstream.close();
}
catch(Exception _ex)
{
byte abyte1[];
abyte0 = CryptoKernel.getB64Hash(abyte1 = CryptoKernel.getRandom(20), "SHA256").getBytes();
try
{
FileOutputStream fileoutputstream;
(fileoutputstream = new FileOutputStream(Utility.getComponentHome() + "global.dat")).write(abyte0);
fileoutputstream.close();
}
catch(Exception _ex2)
{
abyte0 = new byte[44];
for(int j = 0; j < abyte0.length; j++)
abyte0[j] = 25;

}
}
try
{
s = getID(Utility.getComponentHome(), new String(abyte0, "ISO-8859-1"), System.getProperty("os.arch"), System.getProperty("os.name"), System.getProperty("os.version"), System.getProperty("user.name"), System.getProperty("java.vm.vendor"));
String as[] = {
"C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9",
"CA", "CB", "CC", "CD", "CE", "CF", "CG", "CH", "CI", "CJ",
"CK", "CL", "CM", "CN", "80", "81", "82", "83", "84", "85",
"86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
"8G", "8H", "8I", "8J", "8K", "8L", "8M", "8N", "40", "41",
"42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B",
"4C", "4D", "4E", "4F", "4G", "4H", "4I", "4J", "4K", "4L",
"4M", "4N"
};
boolean flag = true;
for(int i = 44; i < s.length(); i += 46)
{
String s1 = s.substring(i, i + 2);
boolean flag1 = false;
for(int k = 0; k < as.length; k++)
{
if(!s1.equals(as[k]))
continue;
flag1 = true;
break;
}

if(flag1)
continue;
flag = false;
break;
}

if(!flag)
{
Debug.log("Illegal PCID (rejected): " + s);
s = "";
}
JNILoader.cleanup();
break MISSING_BLOCK_LABEL_737;
}
catch(UnsatisfiedLinkError _ex)
{
Debug.log("Warning: No PCID generated.");
}
return "";
try
{
return s;
}
catch(Exception exception)
{
Debug.log("Warning: No PCID generated.");
Debug.log(exception);
return "";
}
}

private native String getID(String s, String s1, String s2, String s3, String s4, String s5, String s6);

private native void isLoaded();
}

Anonyymi kirjoitti...

package dk.danskebank.ec.ec.esafekey.businesslogic;

import dk.danskebank.ec.ec.esafekey.utility.Debug;
import dk.danskebank.ec.ec.esafekey.utility.Utility;
import java.io.*;
import java.util.Date;

public class JNILoader
{

public JNILoader()
throws Exception
{
throw new Exception("This class cannot be instantiated");
}

public static synchronized void loadJNI(int i, boolean flag)
throws Exception
{
Exception exception;
switch(i)
{
default:
throw exception = new Exception("Unknown JNI type");

case 1: // '\001'
loadJNI("pcid.dll", flag);
return;

case 2: // '\002'
loadJNI("regdb.dll", flag);
return;

case 3: // '\003'
loadJNI("pkcs11wrapper.dll", flag);
break;
}
}

private static void loadJNI(String s, boolean flag)
throws Exception
{
String s1 = null;
String s2 = createTmpName(s);
switch(Utility.detectOS())
{
default:
Debug.log("Warning: Cannot load jni for unknown operating system");
return;

case 1: // '\001'
s1 = "/jni/win/" + s;
break;

case 2: // '\002'
s1 = "/jni/linux/" + s;
break;

case 3: // '\003'
s1 = "/jni/mac/" + s;
break;
}
File file;
(file = new File(Utility.getComponentHome())).mkdirs();
boolean flag1 = true;
try
{
FileInputStream fileinputstream;
(fileinputstream = new FileInputStream(Utility.getComponentHome() + s2)).close();
File file1;
flag1 = (file1 = new File(Utility.getComponentHome() + s2)).delete();
}
catch(FileNotFoundException _ex) { }
catch(IOException _ex) { }
if(flag1)
writeJNIFile(s1, s2);
try
{
if(flag)
{
System.load(Utility.getComponentHome() + s2);
return;
}
}
catch(UnsatisfiedLinkError _ex)
{
Debug.log("Warning: Could not load file: " + Utility.getComponentHome() + s2);
}
}

public static void cleanup()
{
File file;
if((file = new File(Utility.getComponentHome())).isDirectory())
{
String as[] = file.list();
for(int i = 0; i < as.length; i++)
{
String s;
if((s = as[i]).substring(0, 3).equals("tmp") && s.endsWith(".dll"))
{
File file1 = new File(Utility.getComponentHome() + s);
try
{
file1.delete();
}
catch(Exception _ex)
{
Debug.log("Warning: Could not delete file: " + Utility.getComponentHome() + s);
}
}
}

}
}

public static String createTmpName(String s)
{
Date date;
long l = (date = new Date()).getTime();
return "tmp" + l + s;
}

public static void writeJNIFile(String s, String s1)
{
try
{
InputStream inputstream = dk.danskebank.ec.ec.esafekey.businesslogic.JNILoader.class.getResourceAsStream(s);
FileOutputStream fileoutputstream = new FileOutputStream(Utility.getComponentHome() + s1);
byte abyte0[] = new byte[1024];
for(int i = 0; (i = inputstream.read(abyte0)) != -1;)
fileoutputstream.write(abyte0, 0, i);

inputstream.close();
fileoutputstream.close();
return;
}
catch(Exception _ex)
{
Debug.log("Warning: Could not write file: " + Utility.getComponentHome() + s1);
}
}

public static final int JNI_PCID = 1;
public static final int JNI_REGDB = 2;
public static final int JNI_PKCS11 = 3;
}

Anonyymi kirjoitti...

sampopankki verkko pankki joka ajaa käyttäjän koneella pcid.dll tiedoston javaa käyttäen kerää wmi:llä seuraavat tiedot ja kirjoittaa rekisteriin
[HKEY_CURRENT_USER\"Software\e-SafeKey]
"IStat"=


0002FBD PUSH pcid.100142BC UNICODE "ROOT\CIMV2"
10003069 PUSH pcid.100142AC ASCII "SELECT * FROM "
100030AB PUSH pcid.100142A8 ASCII "WQL"
10003479 PUSH pcid.1001430C ASCII "ProductName"
1000349E PUSH pcid.100142DC ASCII "SOFTWARE\Microsoft\Windows NT\CurrentVersion"
100035EB PUSH pcid.100142D4 ASCII "%.64s"
100036D6 PUSH pcid.10014318 ASCII "ProductId"
100036F8 PUSH pcid.100142DC ASCII "SOFTWARE\Microsoft\Windows NT\CurrentVersion"
1000373B PUSH pcid.100142D4 ASCII "%.64s"
10003801 PUSH pcid.10014330 ASCII "SerialNumber"
10003823 PUSH pcid.10014324 ASCII "Win32_BIOS"
10003883 PUSH pcid.100142D4 ASCII "%.64s"
10003A5B PUSH pcid.1001435C ASCII "GetDiskFreeSpaceExA"
10003A60 PUSH pcid.10014340 UNICODE "kernel32.dll"
10003B81 PUSH pcid.10014384 ASCII "DeviceID"
10003BA3 PUSH pcid.10014374 ASCII "Win32_Battery"
10003C03 PUSH pcid.100142D4 ASCII "%.64s"
10003CC1 PUSH pcid.10014330 ASCII "SerialNumber"
10003CE3 PUSH pcid.10014390 ASCII "Win32_BaseBoard"
10003D43 PUSH pcid.100142D4 ASCII "%.64s"
10003E01 PUSH pcid.10014384 ASCII "DeviceID"
10003E23 PUSH pcid.100143A0 ASCII "Win32_Keyboard"
10003E83 PUSH pcid.100142D4 ASCII "%.64s"
10003F41 PUSH pcid.10014384 ASCII "DeviceID"
10003F63 PUSH pcid.100143B0 ASCII "Win32_SoundDevice"
10003FC3 PUSH pcid.100142D4 ASCII "%.64s"
10004081 PUSH pcid.10014384 ASCII "DeviceID"
100040A3 PUSH pcid.100143C4 ASCII "Win32_USBController"
10004103 PUSH pcid.100142D4 ASCII "%.64s"
100041C1 PUSH pcid.100143F0 ASCII "Model"
100041E3 PUSH pcid.100143D8 ASCII "Win32_ComputerSystem"
10004243 PUSH pcid.100142D4 ASCII "%.64s"
10004329 PUSH pcid.10014424 ASCII "IStat"
1000434E PUSH pcid.10014410 ASCII "Software\e-SafeKey"
10004461 PUSH pcid.10014404 ASCII "e-SafeKey"
1000447F PUSH pcid.100143F8 ASCII "Software"
100044D5 PUSH pcid.10014424 ASCII "IStat"
100044FE PUSH pcid.10014410 ASCII "Software\e-SafeKey"
10004568 PUSH pcid.100142D4 ASCII "%.64s"
10004732 PUSH pcid.1001442C UNICODE "root/cimv2"
10004A81 PUSH pcid.10014478 ASCII "AdapterRAM"
10004AA3 PUSH pcid.10014444 ASCII "Win32_VideoController.DeviceID="VideoController1""
10004B03 PUSH pcid.100142D4 ASCII "%.64s"
10004BC1 PUSH pcid.100144A4 ASCII "ProcessorId"
10004BE3 PUSH pcid.10014484 ASCII "Win32_Processor.DeviceID="CPU0""

ilaiho kirjoitti...

Itse asiassa Nordea muistaakseni näyttää kertakäyttösalasanan järjestysnumeron järjestysnumeron mutta vain, jos syöttää väärän numeron.

Markus Jansson kirjoitti...

Ei näytä kuin siinä tapauksessa, että annettu luku on 2 järjestyslukua pielessä.