Venmo is a very popular mobile app which simplifies payments among friends. Once you link your bank account or credit card, you can start sending money to others, instantly.
With Venmo, you are not limited to just make payments. It allows you to charge others as well. Say your friend had no cash for that tasty burrito and you paid for it. You have the option to be proactive and “charge” your friend using Venmo. Charging someone does not mean that the money will be withdraw from his account, it just means that he will get a notification and see the pending payment in his account. Your friend has to accept the charge in order for the payment to happen. And this functionality is what we are going to take advantage of.
Just as any other mobile app, Venmo has a notification system to keep you up to date with any charges, payments, etc. By default, notifications are processed and rendered by the app. Every time something relevant happens, you will see the familiar alert pop-up on your mobile device screen.
Some time ago, a friend charged me for a bottle of Gin he bought while we were partying at Bay2Breakers. I hate invasive notifications so I have them all disabled. However, instead of the classic “App notification”, I got an SMS. I’ve never notice this before and immediately thought this was a new feature in Venmo. Looking at the it, I realized that it was not just a notification, it was a call to action. The SMS gave me the option to reply with a 6-digit code in order to make the payment. I did so, and as expected the payment was processed.
This had me thinking I hadn’t authenticated to Venmo in order to make the payment. I simply replied to a text with the code I was given and that seemed sketchy.
Stealing money from locked devices
As mentioned above, one of the things you can do with Venmo is send a payment request to someone. It is as simple as paying somebody but instead of clicking “pay”, you click “charge”. The weekly limit is $2,999.99, hence the title of this post.
I remembered that you can use Siri to send SMS when your device is locked. It is worth noting that this feature is on by default and became especially popular when the “Hey Siri” feature was added in iOS 9.
Now that we know we can send SMS on locked devices, we need the code present in the SMS in order to reply and make the payment. Apple introduced the “Text Message Preview” which allows you too see in the lock screen who sent you a text and part of the content. This is also on by default.
If we combine these two, I am able to see the SMS with the code and can reply using Siri. All this without unlocking the device. All this out of the box. WIN?!?
Venmo’s SMS notification
Once I reproduced the attack on my phone, I was very excited. But before running to report it to Venmo, I wanted to make sure it was working on other devices. I tested on my girlfriend’s iPhone and to my surprise, I did not get an SMS. This ruined everything as in order for this attack to be “significant” I was looking to make it happen on devices with default configurations. Yes, I could see texts on the lock screen. Yes, I could reply to texts using Siri. But Venmo’s SMS notification was not enabled by default. I must have enabled it at some point in the past.
I really wanted to make this possible under default settings so I looked at everything all over again. While checking the SMS service, I started to reply with random characters which resulted in another SMS reply like the one below:
When I read the SMS again I realized that I did not pay attention to the most important part: “Reply STOP to cancel”. Hmmm… If I can reply STOP to cancel the SMS notification service, can I send a text with the word START and activate it?
BINGO! You can activate the SMS notification service by sending an SMS to 86753 with the word “Start”. 86753 is a short code number owned by Venmo and used for all the SMS notifications.
Now, I am able to activate Venmo’s SMS notification service, see the secret code and reply to make the payment. All that without unlocking the device!
Venmo’s patch and workaround
When I reported this to Venmo, together with the other findings you can read below, they decided to fix this particular issue by adding additional spaces to the SMS that contains the 6 digit code. Because the Text Preview feature only shows a limited number of characters, Venmo added enough spaces to the SMS to prevent the code from been displayed on the lock screen preview.
Unfortunately, this fix is not good enough. I found a really easy workaround in which I could still find out the code. Again, all I had to do was to use Siri to “Read my latest text”. While I could not read it myself anymore in the lock screen, I can have Siri read it for me. Simple and effective!
Preventing this attack
There are several issues here and to be honest, this is not all Venmo’s fault. The problem is not just that you can pay by replying to an SMS. The problem is also that you can see the text and reply to it without unlocking the device. That is on Apple.
Venmo also let’s you enable the service over SMS and that should not be possible. Venmo should remove that functionality which will prevent this attack entirely (unless you already have it enabled). Given that disabling Venmo’s SMS notifications is not effective, you should disable SMS preview. You can also disable Siri in the lock screen so no one can send texts on your behalf.
Other possible attacks
Now that we know how to abuse the SMS notification system to make money, I wanted to further explore the possibilities of this service. After all, we still need physical access to the mobile device in order to perform this attack. I wanted to see if I could do the same thing but without having to see the SMS, basically brute-forcing the code.
I charged my own account to receive the SMS and started to input incorrect codes. I was expecting to have a number of tries and have the payment be canceled but instead, I got an SMS informing me that I exceeded the number of tries and that I would have to wait a bit to try again. Interesting! The payment is not canceled, there is simply a time based protection in place to make brute-forcing more difficult.
Have you noticed it too? “No one has requested money from you with that confirmation code %s“. Venmo told me there was no security impact, just a typo.
Anyway, the point is, after 5 tries I had to wait about 5 minutes till I could try another 5 times. The codes are six digits long so we have 1 million possibilities and we can try 5 codes every 5 minutes. Do the math. Possible but not feasible.
Codes are linked to payment requests
Since we cannot brute-force the entire code space, I tried to send 2 charge requests instead of one. Basically, I wanted to see if once I send the second charge request, the first one will be invalidated together with the code. Turns out that is not the case. Every charge request is associated to a new code without invalidating previous requests. This means that instead of sending one request and brute-forcing a million codes, I can send multiple charge requests and reduce the number of codes I have to guess!
I used Burp to look at the API and indeed, I was able to send 50 payment requests in a matter of seconds. Of course, this is very noisy. Remember that the victim will get one notification for every charge request… We need to improve this.
One request to multiple victims
My previous approach was to send multiple requests to the same victim in order to reduce the number of codes I have to guess. Since that is too noisy, I can send only one request but to multiple people instead.
User enumeration makes finding victims very simple and it takes just a few lines of code to write a script that will return a million valid Venmo user emails/phone numbers.
Instead of sending a million requests to one victim, we send one request to a million Venmo users. Once we do this, all we have to do is send a text to Venmo’s service with the same code for all the million users. Statistically we should at least have one match. We cannot forget about having to spoof the phone number of the victim when sending the SMS. That is trivially easy with services like Twilio though.
These attacks are theoretical and I did not try them. Venmo payments are known to be monitored and the last thing I want is someone knocking at my door asking why so many people owes me money.
Again, when I reported this to Venmo, they responded that it was not possible because of three reasons:
Short Code numbers cannot be spoofed
Venmo told me that it should not be possible to spoof SMS messages to short codes since they bypass SMS gateways where the spoofing would need to occur. I did not find how to do it either. Because this prevents the attack entirely, I started thinking that a short code number must be associated to a real number. And if I find the real number, I could perform the attack as I explained above because I would be able to spoof the sender.
Doing some tests with the settings in Venmo’s web, I managed to get a text from +1(646)-679-6604 instead of the shortcode 86753. Awesome! I also confirmed that you can get the charge notification through the shortcode number but reply with the 6 digit code to the real number. The payment will still happen making the attack possible again.
rate limiting MAKES user enumeration too expensive
Venmo thinks that it is not possible to enumerate accounts massively since “there is rate limiting in place to prevent abuse of this endpoint“. The problem here is that this is an unauthenticated call (you try to recover the password because you cannot log in) which means, the only way to have rate limits is to check from which IP the multiple requests are coming from. We can easily bypass this protection by using a list of proxies and iterate over them. Our IP will change every few requests without much performance impact (we need max 1 million valid accounts). Venmo should simply return the same message whether the phone number is valid or not.
Daily rate limit to the number of charge requests
Venmo claims that a user can only do a limited number of charge requests per day and that “it would prevent this attack scenario from scaling to the point where it would significantly improve your chances for brute forcing”.
In reality, this makes the attack more tedious rather than impossible. The charge request limits are applied at a per user basis. It is possible to create multiple Venmo accounts and switch between them every time the request limit is reached. My tests indicate that you can make up to 50 charge requests per day. On average, you would need ~1500 accounts to successfully perform this attack. You also need a valid phone number to create an account which makes it trickier.
As I mentioned, I disclosed my findings to Venmo responsibly. They accepted some of the issues I found, proposed patches and mentioned that some of my assumptions were incorrect. I explained above some of the workarounds I found already and I shared that with Venmo as well.
After some back and forth, Venmo took following steps to secure the app:
- They killed the SMS “reply-to-pay” functionality. There was no good way to fix it. I am glad Venmo decided to kill a feature VS keeping it knowing it has flaws.
- The real number cannot be used anymore. You cannot receive an SMS from the shortcode number and reply to the real number now.
- The %s typo in the SMS was fixed
Communication with Venmo’s security team was smooth and professional. They kept me updated on the fixes as those were pushed to production and generally it was a good experience.
- June 1st, 2016: Disclosure to Venmo
- June 8th, 2016: Venmo acknowledges some issues and discards others
- June 10th, 2016: I question some patches and assumptions made by Venmo
- June 15th, 2016: Venmo agrees and decides to kill the “reply-to-pay” feature
- July 18th, 2016: Venmo notifies me that all fixes have been deployed