Posts Tagged ‘Data Leakage’

Samstag, 11. November 2017

Das Alter von AutoScout24-Inseraten herausfinden (Low-Skill-Version)

In den letzten zwei Wochen waren Stephanie und ich auf der Suche nach einem Occasion-Auto als Ersatz für unseren altgedienten TOYOTA Yaris 1.3, den wir an einen Bekannten verkaufen dürfen.

Nach einer Probefahrt an einem Samstag bei einer Garage in der Region drängte uns der Verkäufer dazu, nicht zu Lange mit dem Kaufentscheid zuzuwarten. Die Nachfrage nach solchen Fahrzeugen sei gross und vielleicht könnte bereits jemand weiteres am Montag Interesse anmelden. Beim Sondierungsanruf des Garagisten am darauffolgenden Dienstag-Morgen hatte sich die Prophezeiung dann tatsächlich erfüllt: Ein zweiter Interessent hatte sich materialisiert, aber da wir zuerst angefragt hätten, hätten wir auch das Vorkaufsrecht. Wie anständig!

Verkäufer, die einem unter Zeitdruck setzen wollen, sind mir zunehmends suspekt. Nicht zuletzt, weil ich als Security Officer regelmässig mit Phishing-Mails zu tun habe, deren Autoren mit identischen psychologischen Tricks hantieren: „Alarm, bitte rasch handeln, sonst verlierst du nach 24 oder 48 Stunden Zugang zu deiner Apple ID“. Selbst Booking.com wendet solche Räubermethoden bei ihren Hotelangeboten an („Nur noch 1 Raum zu diesem Preis verfügbar“, „In den letzten 24 Stunden von 3 Personen gebucht“). Da muss man hart bleiben.

Im vorliegenden Fall hatte ich im Hinterkopf, dass ich das probegefahrene Auto vor Wochen bereits auf AutoScout24 gesehen hatte. Wieso also sollte ausgerechnet nun Eile bestehen, überhastete Kaufentscheide zu fällen?

Doch wie konnte ich meine Vermutung verifizieren? AutoScout24 hütet sich natürlich, solche Informationen in Autoanzeigen klar sichtbar anzupreisen. Die AutoScout-Kunden sind nicht wir (für uns ist der Service selbstverständlich kostenlos), sondern die Garagen, die Fahrzeuge verkaufen wollen und dafür AutoScout24 als Mittelmann einen „Wegzoll“ entrichten dürfen. Und wer zahlt befiehlt: Die Informations-Asymmetrie muss gewährleistet bleiben, denn man stelle sich vor, ein Käufer läuft in die Garage hinein und hat im Verhandlungspoker das Wissen in der Hinterhand, dass sein Traumfahrzeug ein Ladenhocker ist.

Wenn uns AutoScout24 also nicht direkt weiterhelfen will, helfen wir uns halt selber. Nach einer Debug-Session an einem dunklen Winterabend hatte ich die Lösung, die im Grunde naheliegend ist: Die Bilder der Autos, und zwar sowohl das Speicherdatum, als auch die im Bild eingebetteten EXIF-Daten.

Wer die Web-Site bereits einmal auseinandergenommen hat, weiss, dass die Bilder nicht als Originale ausgeliefert werden, sondern über ein Script, welchem man in den Parametern den Pfad, die Bilddimensionen und JPEG-Qualität mitgeben kann. Das schaut dann etwa so aus:


https://cas01.autoscout24.ch/toyota-verso-s-kompaktvan–minivan-2015-occasion/?1024×2048/3/90/custom/434/5101434/0.jpg

Die Bausteine lassen sich teilweise entschlüsseln (und beim Fehlen ist auf Grund eines Web-Entwicklers, dem OWASP wohl kein Begriff ist, auch mit hilfreichen Fehlermeldungen zu rechnen):

  • ?1024x2048 = Auflösung in Pixeln
  • /3 = „Quality Format“
  • /90 = Qualität (ein spontaner Test mit „95“ hat nicht funktioniert; d.h. die Werte sind hartkodiert)
  • /custom/434/5101434/0.jpg = Der absolute Pfad der Bilddatei auf dem NFS-Share, wobei ich den Wert 434 nicht deuten kann. Bei 434 handelt es sich um die letzten drei Zahlen der ID. AutoScout24 verwendet diese Zahl, um die Bilder strukturiert in Unterordner abzulegen, um nicht in einem einzigen Verzeichnis Millionen von Photos liegen zu haben. 5101434 ist die ID des Inserats.
    Die ID zählt übrigens stetig hoch. Kennt man also eine ID und ein Publikationsdatum vor diesem Inserat und eine ID und ein Publikationsdatum nach diesem Inserat, kann man den Veröffentlichungszeitpunkt eingrenzen und interpolieren.

Dieses Script stellt zwei Dinge sicher: Einerseits liefert der Web-Server als Erstellungsdatum des Bildes das Datum und die aktuelle Uhrzeit des Aufrufs mit. Andererseits entfernt das Script jegliche EXIF-Daten, welche viele verschiedene Informationen enthalten können, bspw. die GPS-Koordinaten, das Kameramodel, aber natürlich auch das Aufnahmedatum.

Gibt es eine Möglichkeit, auf die Originalbilder zuzugreifen? Selbstverständlich gibt es das, wird aber nirgends verwendet und ist schon gar nicht dokumentiert. Für das Photo, welches ich oben referenziert habe, lautet die URL:


https://cas01.autoscout24.ch/custom/434/5101434/0.jpg

Man benötigt also die Basis-URL https://cas01.autoscout24.ch sowie den String, der an den URLs öffentlich zugänglicher Photos hängt. Hier: /custom/434/5101434/0.jpg.

Lädt man dieses Photo nun unter macOS im Terminal mit wget herunter, liefert der Web-Server das ursprüngliche Speicherdatum mit. Das ursprüngliche Speicherdatum ist identisch mit dem Zeitpunkt, an welchem das Inserat aufgeschaltet wurde:

$ wget "https://cas01.autoscout24.ch/custom/434/5101434/0.jpg"
--2017-11-11 16:51:45--  https://cas01.autoscout24.ch/custom/434/5101434/0.jpg
Auflösen des Hostnamens cas01.autoscout24.ch… 91.208.180.147
Verbindungsaufbau zu cas01.autoscout24.ch|91.208.180.147|:443 … verbunden.
HTTP-Anforderung gesendet, auf Antwort wird gewartet … 200 OK
Länge: 542433 (530K) [image/jpeg]
Wird in »0.jpg« gespeichert.

0.jpg               100%[===================>] 529,72K  --.-KB/s    in 0,1s    

2017-11-11 16:51:47 (4,95 MB/s) - »0.jpg« gespeichert [542433/542433]

Das Upload-Datum der Datei lautet 31. August 2017, 08:50 Uhr morgens:

$ ls -l
total 1064
-rw-r--r--  1 user  staff  542433 31 Aug 08:50 0.jpg

Das ist realistisch — der 31. August war ein Donnerstag und 8:50 Uhr liegt innerhalb der Bürozeiten.

Und was sagt jhead, mit welchem man EXIF-Informationen auslesen kann?

$ jhead 0.jpg 
File name    : 0.jpg
File size    : 542433 bytes
File date    : 2017:08:31 08:50:08
Resolution   : 2048 x 1365
JPEG Quality : 89

Das sind leider keine EXIF-Informationen: AutoScout entfernt diese offenbar aus den Originalen, die sie im oben genannten Pfad ablegen.

Und so hatte ich mich in diesem konkreten Fall selber beruhigt, dass keine Hüst-und-Hott-Aktion nötig sein würde. Die Wahrscheinlichkeit, dass das als Schnäppchen angepriesene Auto zwei Monate lang herumstand und sich urplötzlich am selben Wochenende zwei Parteien dafür interessieren würden, war doch äusserst gering und wohl die Erfindung eines provisionsgetriebenen Verkäufers.

NB: Finde ich die Zeit, werde ich in einem zukünftigen Artikel beschreiben, wie die High-Skill-Version ausschaut. Kurzzusammenfassung: iOS-App, mitmproxy und JSON. So findet man alle möglichen Meta-Informationen zu einem Inserat, die man als Endbenutzer selbst in der App, aber auch auf der Web-Site nie zu Gesicht bekommt respektive nur in gefilterter/formatierter Version.

Tags: , , , , , ,
Labels: Web

30 Kommentare | neuen Kommentar verfassen

Samstag, 14. Oktober 2017

upc cablecoms Verfügbarkeitsabfrage ist äusserst geschwätzig

upc cablecom bietet auf ihrer Web-Site die Möglichkeit, eine Wohnadresse auf die Verfügbarkeit von upc-Produkten zu überprüfen:

Verfügbarkeit prüfen

Füllt man die erforderlichen Angaben in das Formular ein, erhält man eine rein binäre Antwort für jede der drei Produktkategorien Internet, TV und Festnetz:

Im Hintergrund setzt dafür JavaScript eine Abfrage auf eine upc API ab. Die URL lautet für das vorliegende Beispiel:

https://www.upc.ch/aff-upc-cablecom-ch/aav/availability.json?streetName=Schl%C3%B6sslistrasse&streetNumber=39&postalCode=3008&_charset_=UTF-8&lang=de

Als Antwort erhält man eine Datei namens availability.json zurück. Nachfolgend das Beispiel für meine Wohnadresse:

{
    "addressResultState": "uniqueAddress",
    "addresses": [
        {
            "buildingNumber": 39,
            "buildingNumberAnnex": null,
            "buildingStatusId": 3,
            "buildingStatusName": "completed",
            "communeName": "Bern",
            "coordinateX": "599190",
            "coordinateY": "199475",
            "countryCodeA2": "CH",
            "idBuilding": 131767,
            "locationName1": "Bern",
            "locationName2": "Bern",
            "networkOperatorId": 0,
            "postCode": "3008",
            "postCodeAnnex": "00",
            "postOfficeName": "Bern",
            "stateName": "Bern/Berne",
            "stateShort": "BE",
            "streetName1": "Schl\u00f6sslistrasse"
        }
    ],
    "availability": {
        "accessProvider": null,
        "accessTechnology": null,
        "cablecomRegion": "Nord-West",
        "catv": {
            "available": true,
            "houseUpgradeNeeded": false,
            "packages": {
                "_package": [
                    "not available"
                ]
            },
            "partnerNetFlag": false
        },
        "dp": {
            "available": true,
            "availableFrom": {
                "day": 5,
                "fractionalSecond": 0.0,
                "hour": 0,
                "minute": 0,
                "month": 9,
                "second": 0,
                "timezone": 120,
                "year": 2000
            },
            "houseUpgradeNeeded": false,
            "packages": {
                "_package": [
                    "RVO_VSB01",
                    "RVO_VSB02",
                    "RVO_VSB03",
                    "RVO_VSB04",
                    "RVO_VSB05",
                    "RVO_FRC1",
                    "RVO_GBL1",
                    "RVO_FCG1",
                    "RVO_FRC2",
                    "RVO_GBL2",
                    "RVO_FCG2",
                    "RVO_FCE1",
                    "RVO_FCE2",
                    "RVO_FPW1",
                    "RVO_FPW2",
                    "RVO_FPS1",
                    "RVO_FPS2",
                    "RVO_FPP1",
                    "RVO_FPP2",
                    "RVO_BP1",
                    "RVO_BDS13",
                    "RVO_BDS14",
                    "RVO_BDS08",
                    "RVO_BDS11",
                    "RVO_BDS09",
                    "RVO_BDS12"
                ]
            },
            "partnerNetFlag": false
        },
        "dtv": {
            "available": true,
            "availableFrom": {
                "day": 1,
                "fractionalSecond": 0.0,
                "hour": 0,
                "minute": 0,
                "month": 1,
                "second": 0,
                "timezone": 60,
                "year": 1999
            },
            "houseUpgradeNeeded": false,
            "packages": {
                "_package": [
                    "",
                    "NR_SVD01",
                    "RDC_SFRSD",
                    "RDC_SFRS_M",
                    "RDI_CH01",
                    "RDI_CH04",
                    "RDI_CH05",
                    "RDI_CH06",
                    "RDI_CH07",
                    "RDI_CH08",
                    "RDI_CH09",
                    "RDI_CH09",
                    "RDI_CH10",
                    "RDI_CH11",
                    "RDI_CH17",
                    "RDI_CH17H",
                    "RDI_CH18",
                    "RDI_CH18H",
                    "RDI_CH19",
                    "RDI_CH19H",
                    "RDI_CH20",
                    "RDI_CH20H",
                    "RDI_CH21",
                    "RDI_CH21",
                    "RDI_CH21_M",
                    "RDI_CH21_M",
                    "RDI_CH22",
                    "RDI_CH22_M",
                    "RDI_CH24",
                    "RDI_CH24_M",
                    "RDI_CH25",
                    "RDI_CH25_M",
                    "RDI_CH26",
                    "RDI_CH26_M",
                    "RDI_CH27",
                    "RDI_CH27_M",
                    "RDI_CH28",
                    "RDI_CH28_M",
                    "RDI_CH30",
                    "RDI_CH31",
                    "RDI_CH32",
                    "RDI_CH33",
                    "RDI_CH40",
                    "RDI_CH40_M",
                    "RDI_CH41",
                    "RDI_CH41",
                    "RDI_CH42",
                    "RDI_CH44",
                    "RDI_CH47",
                    "RDI_CH47",
                    "RDI_CH48",
                    "RDI_CH49",
                    "RDI_CH50",
                    "RDI_CH51",
                    "RDI_CH52",
                    "RDI_CH53",
                    "RDI_CH54",
                    "RDI_CH55",
                    "RDI_CH56",
                    "RDI_CH57",
                    "RDI_CH59",
                    "RDI_CH60",
                    "RDI_CH61",
                    "RDI_CH62",
                    "RDI_CH65",
                    "RDI_CH66",
                    "RDI_CH67",
                    "RDI_CH68",
                    "RDI_CH72",
                    "RDI_CH73",
                    "RDI_CH74",
                    "RDI_CH81",
                    "RDI_CH93",
                    "RDI_CH97_M",
                    "RDI_D4A_97",
                    "RDI_DUMW",
                    "RDI_DWISR1",
                    "RDI_DWISS1",
                    "RDI_DWR1",
                    "RDI_DWS1",
                    "RDI_HCOD",
                    "RDI_HRZ06",
                    "RDI_HRZ11",
                    "RDI_HRZ17",
                    "RDI_HRZ17",
                    "RDI_CH81",
                    "RDI_HRZ17H",
                    "RDI_HRZ81",
                    "RDI_HRZ18",
                    "RDI_HRZ18H",
                    "RDI_HRZ19",
                    "RDI_HRZ19H",
                    "RDI_HRZ20",
                    "RDI_HRZ20H",
                    "RDI_HRZ21",
                    "RDI_HRZ21",
                    "RDI_HRZ22",
                    "RDI_HRZ24",
                    "RDI_HRZ25",
                    "RDI_HRZ26",
                    "RDI_HRZ27",
                    "RDI_HRZ28",
                    "RDI_HRZ40",
                    "RDI_HRZ52",
                    "RDI_HRZ53",
                    "RDI_HRZ83",
                    "RDI_HRZCL",
                    "RDI_HRZCO",
                    "RDI_HRZDS",
                    "RDI_HRZ_97",
                    "RDI_PREM1",
                    "RDI_PREM2",
                    "RDI_PREM_M",
                    "RDI_PRM",
                    "RDI_PRMM",
                    "RDI_SPTV",
                    "RDI_SPTV_M",
                    "RDI_SVD_51",
                    "RDI_SVD_51",
                    "RDI_SVD_51",
                    "RDI_SVD_52",
                    "RDI_SVD_52",
                    "RDI_SVD_52",
                    "RDI_SVD_59",
                    "RDI_SVD_60",
                    "RHZ_CLAS7",
                    "RHZ_CLAS8",
                    "RHZ_PREM1",
                    "RHZ_PREM2",
                    "RHZ_PRM",
                    "RHZ_SFRSH",
                    "RHZ_SPTV",
                    "RDI_CTDV",
                    "RDI_DUMS",
                    "RDI_DUMM"
                ]
            },
            "partnerNetFlag": false
        },
        "dtvActicationCode": "203",
        "dtvDistributionModel": "Vertriebsmodell Cablecom",
        "dtvSetupId": "01810000",
        "ftthBuildingStatus": null,
        "ftthSla": null,
        "hs": {
            "available": true,
            "availableFrom": {
                "day": 5,
                "fractionalSecond": 0.0,
                "hour": 0,
                "minute": 0,
                "month": 9,
                "second": 0,
                "timezone": 120,
                "year": 2000
            },
            "houseUpgradeNeeded": false,
            "packages": {
                "_package": [
                    "DHS_BSO",
                    "RHS_BDS06",
                    "RHS_BDS07",
                    "RHS_BDS08",
                    "RHS_BDS09",
                    "RHS_BDS10",
                    "RHS_BDS11",
                    "RHS_BDS12",
                    "RHS_BDS13",
                    "RHS_BDS14",
                    "RHS_BDS15",
                    "RHS_BDS16",
                    "RHS_BDS17",
                    "RHS_BDS18",
                    "RHS_BDS20",
                    "RHS_BDS21",
                    "RHS_HC256",
                    "RHS_HS02",
                    "RHS_HS05",
                    "RHS_HS06",
                    "RHS_HS08",
                    "RHS_HS09",
                    "RHS_HS11",
                    "RHS_HS12",
                    "RHS_HS13",
                    "RHS_HS14",
                    "RHS_HS15",
                    "RHS_HS16",
                    "RHS_HS17",
                    "RHS_HSED13",
                    "RHS_HSED21",
                    "RHS_HSED30",
                    "RHS_HSED32",
                    "RHS_NDH25",
                    "RHS_HSED10",
                    "RHS_HSED11",
                    "RHS_HSED12",
                    "RHS_HSED14",
                    "RHS_HSED15",
                    "RHS_HSED16",
                    "RHS_HSED17",
                    "RHS_HSED18",
                    "RHS_HSED19",
                    "RHS_HSED20",
                    "RHS_HSED22",
                    "RHS_HSED23",
                    "RHS_HSED24",
                    "RHS_HSED25",
                    "RHS_HSED26",
                    "RHS_HSED28",
                    "RHS_HSED29",
                    "RHS_WOO"
                ]
            },
            "partnerNetFlag": false
        },
        "idBuilding": 131767,
        "languageRegion": "DE",
        "longText": "An Ihrem Standort ist bereits alles vorbereitet. Melden Sie sich jetzt online an und schon in wenigen Tagen k\u00f6nnen Sie vom gew\u00fcnschten Produkt profitieren.",
        "mobile": {
            "available": true,
            "houseUpgradeNeeded": false,
            "packages": {
                "_package": [
                    "ALL"
                ]
            },
            "partnerNetFlag": false
        },
        "vod": {
            "available": true,
            "houseUpgradeNeeded": false,
            "packages": {
                "_package": [
                    "BM_VODKIT"
                ]
            },
            "partnerNetFlag": false
        }
    },
    "availabilityResultState": "availabilityFound"
}

(Lesbar gemacht mit Google Chrome unter die Haube schauen (und JSON lesbar ausgeben))

Einige Erkenntnisse:

  • Diese API rechnet Strassenanschriften ins Schweizer Koordinatensystem um (600:200, irgendjemand?).
  • Das JSON sagt auch, wann das Gebäude an das cablecom-Netz angeschlossen wurde (Internet am 5. September 2000, Fernsehen am 1. Januar 1999 (wahrscheinlich ein Dummy-Wert, oder aber cablecom hat zu dem Stichtag das Berner Kabelnetz übernommen?)).
  • Benötigt mein Gebäude ein Upgrade? Aus meiner Sicht ein möglicher Hinweis auf Probleme in der Hausverkabelung und somit auf mögliche Geschwindigkeitseinbussen.
  • Es gibt drei Produktekategorien: dtv steht für Digital TV, hs steht für Highspeed (Internet) und dp wohl für Digital Phone.
  • Für jede Produktkategorie wird fein säuberlich aufgelistet, welche Packages verfügbar sind. Leider ist es mir nicht möglich, die Abkürzungen dieser Packages zu entziffern. Bei Digital TV vermute ich, dass einzelne Sender aufgelistet werden. Bei Highspeed Internet könnten es die effektiv verfügbaren Geschwindigkeiten sein.
  • Im JSON sind auch der Activation Code und die Setup ID aufgeführt, die man auf der Horizon-Box oder dem TV-Gerät für die Kanalsuche eingeben muss.
  • Schlussendlich sieht man auch, ob ein Gebäude an das (upc?) Glasfasernetz angeschlossen ist.

Tags: , , , , , ,
Labels: IT, Schweiz, Shopping

Keine Kommentare | neuen Kommentar verfassen