#1# Copyright (c) 2006-2025 Wade Alcorn - [email protected]2# Browser Exploitation Framework (BeEF) - https://beefproject.com3# See the file 'doc/COPYING' for copying permission4#5require 'test/unit'6require 'rest-client'7require 'json'8require '../common/test_constants'910class TC_DnsRest < Test::Unit::TestCase1112class << self1314def startup15json = {:username => BEEF_USER, :password => BEEF_PASSWD}.to_json16@@headers = {:content_type => :json, :accept => :json}1718response = RestClient.post("#{RESTAPI_ADMIN}/login",19json,20@@headers)2122result = JSON.parse(response.body)23@@token = result['token']2425$root_dir = '../../'26$:.unshift($root_dir)2728require 'core/loader'2930BeEF::Core::Configuration.new(File.join($root_dir, 'config.yaml'))31BeEF::Core::Configuration.instance.load_extensions_config3233@@config = BeEF::Core::Configuration.instance34end3536def shutdown37$root_dir = nil38end3940end4142# Tests POST /api/dns/rule handler with valid input43def test_1_add_rule_good44pattern = 'foo.bar'45resource = 'A'46dns_response = ['1.2.3.4']4748json = {:pattern => pattern, :resource => resource, :response => dns_response}.to_json4950rest_response = RestClient.post("#{RESTAPI_DNS}/rule?token=#{@@token}",51json,52@@headers)5354check_rest_response(rest_response)5556result = JSON.parse(rest_response.body)57first_id = result['id']5859rest_response = RestClient.post("#{RESTAPI_DNS}/rule?token=#{@@token}",60json,61@@headers)6263# Verify that adding an existing rule returns its id64check_rest_response(rest_response)6566result = JSON.parse(rest_response.body)67second_id = result['id']6869assert_equal(first_id, second_id)70end7172# Tests POST /api/dns/rule handler with invalid input73def test_2_add_rule_bad74pattern = ''75resource = 'A'76dns_response = ['1.1.1.1']7778hash = {:pattern => pattern, :resource => resource, :response => dns_response}7980# Test that an empty "pattern" key returns 40081assert_raise RestClient::BadRequest do82rest_response = RestClient.post("#{RESTAPI_DNS}/rule?token=#{@@token}",83hash.to_json,84@@headers)85end8687hash['pattern'] = 'foo.bar.baz'88hash['resource'] = ''8990# Test that an empty "resource" key returns 40091assert_raise RestClient::BadRequest do92rest_response = RestClient.post("#{RESTAPI_DNS}/rule?token=#{@@token}",93hash.to_json,94@@headers)95end9697hash['resource'] = 'A'98hash['response'] = []99100# Test that an empty "response" key returns 400101assert_raise RestClient::BadRequest do102rest_response = RestClient.post("#{RESTAPI_DNS}/rule?token=#{@@token}",103hash.to_json,104@@headers)105end106107hash['response'] = 42108109# Test that a non-array "response" key returns 400110assert_raise RestClient::BadRequest do111rest_response = RestClient.post("#{RESTAPI_DNS}/rule?token=#{@@token}",112hash.to_json,113@@headers)114end115end116=begin117# Tests POST /api/dns/rule handler with each supported RR type118def test_3_add_rule_types119pattern = 'be.ef'120resource = 'AAAA'121response = ['2001:db8:ac10:fe01::']122123# Test AAAA type124rule = {'pattern' => pattern, 'resource' => resource, 'response' => response}125126regex = %r{127^#{rule['pattern']}\.\t+128\d+\t+129IN\t+130#{rule['resource']}\t+131#{rule['response'][0]}$132}x133134add_rule(rule)135check_dns_response(regex, rule['resource'], rule['pattern'])136137# Test CNAME type138rule['resource'] = 'CNAME'139rule['response'] = ['fe.eb.']140141regex = %r{142^#{rule['pattern']}\.\t+143\d+\t+144IN\t+145#{rule['resource']}\t+146#{rule['response'][0]}$147}x148149add_rule(rule)150check_dns_response(regex, rule['resource'], rule['pattern'])151152# Test HINFO type153rule['resource'] = 'HINFO'154rule['response'] = ['M6800', 'VMS']155156regex = %r{157^#{rule['pattern']}\.\t+158\d+\t+159IN\t+160#{rule['resource']}\t+161"#{rule['response'][0]}"\s+162"#{rule['response'][1]}"$163}x164165add_rule(rule)166check_dns_response(regex, rule['resource'], rule['pattern'])167168# Test MINFO type169rule['resource'] = 'MINFO'170rule['response'] = ['rmail.be.ef.', 'email.be.ef.']171172regex = %r{173^#{rule['pattern']}\.\t+174\d+\t+175IN\t+176#{rule['resource']}\t+177#{rule['response'][0]}\s+178#{rule['response'][1]}$179}x180181add_rule(rule)182check_dns_response(regex, rule['resource'], rule['pattern'])183184# Test MX type185rule['resource'] = 'MX'186rule['response'] = [10, 'mail.be.ef.']187188regex = %r{189^#{rule['pattern']}\.\t+190\d+\t+191IN\t+192#{rule['resource']}\t+193#{rule['response'][0]}\s+194#{rule['response'][1]}$195}x196197add_rule(rule)198check_dns_response(regex, rule['resource'], rule['pattern'])199200# Test NS type201rule['resource'] = 'NS'202rule['response'] = ['ns.be.ef.']203204regex = %r{205^#{rule['pattern']}\.\t+206\d+\t+207IN\t+208#{rule['resource']}\t+209#{rule['response'][0]}$210}x211212add_rule(rule)213check_dns_response(regex, rule['resource'], rule['pattern'])214215# Test PTR type216rule['resource'] = 'PTR'217rule['response'] = ['4.3.2.1.in-addr.arpa.']218219regex = %r{220^#{rule['pattern']}\.\t+221\d+\t+222IN\t+223#{rule['resource']}\t+224#{rule['response'][0]}$225}x226227add_rule(rule)228check_dns_response(regex, rule['resource'], rule['pattern'])229230# Test SOA type231rule['resource'] = 'SOA'232rule['response'] = [233"ns.#{rule['pattern']}.",234"mail.#{rule['pattern']}.",2352012031500,23610800,2373600,238604800,2393600240]241242regex = %r{243^#{rule['pattern']}\.\t+244\d+\t+245IN\t+246#{rule['resource']}\t+247.*248}x249250add_rule(rule)251check_dns_response(regex, rule['resource'], rule['pattern'])252253# Test TXT type254rule['resource'] = 'TXT'255rule['response'] = ['b33f_is_s0_l33t']256257regex = %r{258^#{rule['pattern']}\.\t+259\d+\t+260IN\t+261#{rule['resource']}\t+262"#{rule['response'][0]}"$263}x264265add_rule(rule)266check_dns_response(regex, rule['resource'], rule['pattern'])267268# Test WKS type269rule['resource'] = 'WKS'270rule['response'] = ['9.9.9.9', 6, 0]271272regex = %r{273^#{rule['pattern']}\.\t+274\d+\t+275IN\t+276#{rule['resource']}\t+277#{rule['response'][0]}\s2780\s5\s6$279}x280281add_rule(rule)282check_dns_response(regex, rule['resource'], rule['pattern'])283284# Test that an invalid RR returns 400285rule['resource'] = 'BeEF'286287assert_raise RestClient::BadRequest do288rest_response = RestClient.post("#{RESTAPI_DNS}/rule?token=#{@@token}",289rule.to_json,290@@headers)291end292end293=begin294# Tests GET /api/dns/rule/:id handler with valid input295def test_4_get_rule_good296pattern = 'wheres.the.beef'297resource = 'A'298dns_response = ['4.2.4.2']299300json = {:pattern => pattern, :resource => resource, :response => dns_response}.to_json301302rest_response = RestClient.post("#{RESTAPI_DNS}/rule?token=#{@@token}",303json,304@@headers)305306check_rest_response(rest_response)307result = JSON.parse(rest_response.body)308id = result['id']309310rest_response = RestClient.get("#{RESTAPI_DNS}/rule/#{id}", :params => {:token => @@token})311312assert_not_nil(rest_response.body)313assert_equal(200, rest_response.code)314315result = JSON.parse(rest_response.body)316317assert_equal(id, result['id'])318assert_equal(pattern, result['pattern'])319assert_equal(resource, result['resource'])320assert_equal(dns_response, result['response'])321end322323# Tests GET /api/dns/rule/:id handler with invalid input324def test_5_get_rule_bad325id = 42326327assert_raise RestClient::ResourceNotFound do328response = RestClient.get("#{RESTAPI_DNS}/rule/#{id}", :params => {:token => @@token})329end330331id = '(*_*)'332333assert_raise RestClient::BadRequest do334RestClient.get("#{RESTAPI_DNS}/rule/#{id}", :params => {:token => @@token})335end336end337338# Tests GET /api/dns/ruleset handler339def test_6_get_ruleset340rest_response = RestClient.get("#{RESTAPI_DNS}/ruleset", :params => {:token => @@token})341342assert_not_nil(rest_response.body)343assert_equal(200, rest_response.code)344345result = JSON.parse(rest_response.body)346assert_equal(15, result['count'])347348result['ruleset'].each do |rule|349assert(rule['id'])350assert(rule['pattern'])351assert(rule['resource'])352assert(rule['response'].length != 0)353end354end355=end356private357358# Adds a new DNS rule359def add_rule(params)360response = RestClient.post("#{RESTAPI_DNS}/rule?token=#{@@token}",361params.to_json,362@@headers)363364check_rest_response(response)365end366367# Standard assertions for verifying response from RESTful API368def check_rest_response(response)369assert_not_nil(response.body)370assert_equal(200, response.code)371372result = JSON.parse(response.body)373374assert(result['success'])375assert(result['id'])376end377378# Compares output of dig command against regex379def check_dns_response(regex, type, pattern)380address = @@config.get('beef.extension.dns.address')381port = @@config.get('beef.extension.dns.port')382383dig_output = IO.popen(["dig", "@#{address}", "-p", "#{port}", "-t", "#{type}", "#{pattern}"], 'r+').read384assert_match(regex, dig_output)385end386387end388389390