Sending USSD codes with curl on ZTE MF823 modem

I have ZTE MF823 LTE modem and it's been working really well in my home network for a while now but recently I've changed my network provider and to have my "internets" really cheep I need to send an USSD code every month (special offer expires every 30 days). That gave me idea for a small script that would check if the promo is active and if not then it would send out USSD code automaticly.

ZTE MF823 inteface:

Here's some information I was able to get about ZTE MF823 interface.

So if someone feels particullary interesting what's sitting inside it you can log in to it using telnet:

telnet 192.168.0.1

login: root

password: zte9x15

so if someone is interested what's behind the scenes there is an option to do that. I looked in there a couple of times but decided to first focus on what I needed to do.

The configuration that I need (including sending USSD codes) can be done using web interface so I went straight to Google Chrome Developer Tools and this is what I managed to find.

All the changes are done with POST to this URL:

http://192.168.0.1/goform/goform_set_cmd_process

I've also saw that Referer in the header was mandatory for the changes to work:

-H 'Referer: http://192.168.0.1/index.html'

I've left some additional things (like Host in the header and compressed) just to be on the safe side (didn't have enouth time for testing).

Actions are controlled by setting parameters to a right value. More about parameters and values later.

My modem is working in LTE Only mode and can't send USSD codes in this mode so fist I need to switch it to WCDMA or auto but I decided that I want to have more control so I'm switching to WCDMA.

First disconnect:

curl -s 'http://192.168.0.1/goform/goform_set_cmd_process' -H 'Host: 192.168.0.1' -H 'Referer: http://192.168.0.1/index.html' --compressed --data 'goformId=DISCONNECT_NETWORK' 

setting goformId=DISCONNECT_NETWORK will disconect the network.

Change mode:

curl -s 'http://192.168.0.1/goform/goform_set_cmd_process' -H 'Host: 192.168.0.1' -H 'Referer: http://192.168.0.1/index.html' --compressed --data 'goformId=SET_BEARER_PREFERENCE&BearerPreference=Only_WCDMA'

So here I'm changing connection from "LTE Only" to "WCDMA Only". Other possible options for my modem are: Only_LTE Only_WCDMA Only_GSM NETWORK_auto So to get this parameters telnet access was very handly. Found this on some website (in cyrylic) that parameters are located in /usr/bin/zte* files and that's where I found these options. In /usr/bin/zte_wan_nwinfor

And to connect again:

curl -s 'http://192.168.0.1/goform/goform_set_cmd_process' -H 'Host: 192.168.0.1' -H 'Referer: http://192.168.0.1/index.html' --compressed --data 'goformId=CONNECT_NETWORK'

Now my modem is ready to send USSDs. And here comes a moment when it becomes ugly because when I was testing this I found out that my operator has a kind of USSD DDoS protection and if you send to many codes you'll get a ban (for a while). That's why this doesn't look very nice (yet):

curl -s -0 "http://192.168.0.1/goform/goform_set_cmd_process" -H "Host: 192.168.0.1" -H "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0" -H "Accept: application/json, text/javascript, */*; q=0.01" -H "Accept-Language: en-US,en;q=0.5" --compressed -H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" -H "X-Requested-With: XMLHttpRequest" -H "Referer: http://192.168.0.1/index.html" -H "Connection: keep-alive" -H "Pragma: no-cache" -H "Cache-Control: no-cache" --data "isTest=false&goformId=USSD_PROCESS&USSD_operator=ussd_send&USSD_send_number=*111*480*3"%"23&notCallback=true"

So the important part for me in this long nudle was:

USSD_send_number=*111*480*3"%"23

After this is sent web UI is sending this:

curl -s "http://192.168.0.1/goform/goform_get_cmd_process?cmd=ussd_write_flag&_=$(date +s)" -H 'Host: 192.168.0.1' -H 'Referer: http://192.168.0.1/index.html'

every second. This is not exactly what the modem is sending but modified a bit version that I'm sending (date is in Epoch BTW).

So at first I was woundering what it was and I think it's getting status of the USSD code (if there's an answer or not or a status from operator). Sample answers: {"ussd_write_flag":"15"} - waiting for answer. So this is not bad but we have to wait a bit longer. {"ussd_write_flag":"16"} - answer ready

And once I got: {"ussd_write_flag":"4"} - This is timeout. I was getting this when I sent a lot of requests.

Once we get flag 16 we can read the answer from the operator. This is the way to do it:

curl -s "http://192.168.0.1/goform/goform_get_cmd_process?cmd=ussd_data_info&_=$(date +%s)" -H 'Host: 192.168.0.1' -H 'Referer: http://192.168.0.1/index.html'

The response should look similar to this:

{"ussd_action":"0","ussd_dcs":"72","ussd_data":"0050006F007A006F007300740061006C006F002000430069002000350037002C00300030007A006C00200064006F002000770079006B006F0072007A0079007300740061006E0069006100200064006F00200032003000310036002D00300035002D00300039002000320033003A00350039003A00350039002E00200044006F006400610074006B006F0077006F00200077002000720061006D006100630068002000730072006F0064006B006F0077002000700072006F006D006F00630079006A006E0079006300680020006D00610073007A00200037002C00340031007A006C00200064006F002000770079006B006F0072007A0079007300740061006E0069006100200064006F00200032003000310036002D00300032002D00320039002000320033003A00350039003A00350039002E"}

Not going to pretend what ussd_action mean. USSD DCS 72 is encoding of the message. 72 means UCS-2. I got it to more readable format with this:

curl -s "http://192.168.0.1/goform/goform_get_cmd_process?cmd=ussd_data_info&_=$(date +%s)" -H 'Host: 192.168.0.1' -H 'Referer: http://192.168.0.1/index.html' | sed -r 's/.+ussd_data":"([0-9a-fA-F]+)"}/\1/; s/0{2}//g' | xxd -r -p

Getting the USSD answer from the modem, then getting HEX that's after ussd_data, removing 00 and converting hex to some "real" chars with xxd :)

So mine is saying:

Usluga wlaczona 0 Wyjscie

That means that I'm good for now and should check the status tomorrow :)