diff --git a/README.md b/README.md index ade2fd0..a4c1725 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Usage This is the help from dines. - Dines 0.3 - The definitive DNS packet forger. + Dines 0.4 - The definitive DNS packet forger. Fields with (F) can be fuzzed. (Example --txid F) Fields with (R) are repeatable. (Example --answer) @@ -69,6 +69,10 @@ To generate a question, issue the follogin command: that asks for domain www.test.com, sending 1 packet only. To generate an answer, one can use the following command - sudo ./dines --src-ip 192.168.1.1 --dst-ip 192.168.1.2 --question www.google.com,1,1 --num 1 --answer www.google.com,1,1,256,$'\xc0\xa8\01\01' --answer www.google.com,1,1,256,$'\xc0\xa8\01\02' + sudo ./dines --src-ip 192.168.1.1 --dst-ip 192.168.1.2 --question www.test.com,1,1 --num 1 --rdata-ip --answer www.test.com,1,1,256,192.168.1.1 --answer www.test.com,1,1,256,192.168.1.2 -The switches related to resource records can be repeated multiple times. +The switches related to resource records can be repeated multiple times. In order to specify IP addresses on +the command line, the user must use the --rdata-ip switch. Otherwise, data will we used as is. This allows +to inject binary data directly, as in the following example + + sudo ./dines --src-ip 192.168.1.1 --dst-ip 192.168.1.2 --question www.test.com,NULL,IN --num 1 --answer www.test.com,NULL,IN,0,$'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a' diff --git a/convert.cpp b/convert.cpp index 91c8238..666c442 100644 --- a/convert.cpp +++ b/convert.cpp @@ -34,6 +34,7 @@ uint16_t stringToQtype(const std::string& s) if (s == "A") return 1; if (s == "NS") return 2; if (s == "CNAME") return 5; + if (s == "NULL") return 10; if (s == "PTR") return 12; if (s == "HINFO") return 13; if (s == "MX") return 15; @@ -47,7 +48,7 @@ uint16_t stringToQtype(const std::string& s) unsigned n = atoi(s.c_str()); if (n > 0xFFFF || n == 0) { - throw runtime_error(string(__func__) + "Invalid qtype"); + throw runtime_error(string(__func__) + ": Invalid qtype"); } return n; @@ -62,6 +63,8 @@ string qtypeToString(uint16_t qtype) return "NS"; case 5: return "CNAME"; + case 10: + return "NULL"; case 12: return "PTR"; case 13: @@ -75,7 +78,7 @@ string qtypeToString(uint16_t qtype) case 255: return "ANY"; default: - throw logic_error(string(__func__) + "Invalid qtype"); + throw logic_error(string(__func__) + ": Invalid qtype"); } } @@ -91,7 +94,7 @@ uint16_t stringToQclass(const std::string& s) if (s == "F") return 1; // Invalid class - throw runtime_error(string(__func__) + "Invalid qclass"); + throw runtime_error(string(__func__) + ": Invalid qclass"); } string qclassToString(uint16_t qclass) @@ -110,6 +113,6 @@ string qclassToString(uint16_t qclass) case 255: return "ANY"; default: - throw logic_error(string(__func__) + "Invalid qclass"); + throw logic_error(string(__func__) + ": Invalid qclass"); } } diff --git a/dns_packet.cpp b/dns_packet.cpp index db8e0a9..328327a 100644 --- a/dns_packet.cpp +++ b/dns_packet.cpp @@ -46,6 +46,8 @@ DnsPacket::DnsPacket(Dines::LogFunc l) _fuzzSrcIp = false; _fuzzSport = false; + _rDataIp = false; + _log = l; if (_log != NULL) @@ -290,25 +292,35 @@ DnsQuestion& DnsPacket::addQuestion(const std::string qdomain, unsigned qtype, u return _question; } -ResourceRecord& DnsPacket::addRR(Dines::RecordSection section, const std::string& rrDomain, unsigned rrType, - unsigned rrClass, unsigned ttl, const char* rdata, unsigned rdatalen) +ResourceRecord& DnsPacket::addRR(Dines::RecordSection section, const std::string& rrDomain, + unsigned rrType, unsigned rrClass, unsigned ttl, const char* rdata, unsigned rdatalen) { string rd(rdata, rdatalen); return addRR(section, rrDomain, rrType, rrClass, ttl, rd); } ResourceRecord& DnsPacket::addRR(Dines::RecordSection section, const std::string rrDomain, - const std::string& rrType, const std::string& rrClass, const std::string& ttl, const std::string& rdata) + const std::string& rrType, const std::string& rrClass, const std::string& ttl, + const std::string& rdata) { unsigned type = stringToQtype(rrType); unsigned klass = stringToQclass(rrClass); unsigned int_ttl = atoi(ttl.data()); - return addRR(section, rrDomain, type, klass, int_ttl, rdata); + string localrdata = rdata; + + if (_rDataIp) { + if (_log) + _log("Converting " + rdata + " to IP address"); + uint32_t addr = _convertIP(rdata); + localrdata = string((char*)&addr, 4); + } + + return addRR(section, rrDomain, type, klass, int_ttl, localrdata); } -ResourceRecord& DnsPacket::addRR(Dines::RecordSection section, const std::string& rrDomain, unsigned rrType, - unsigned rrClass, unsigned ttl, const std::string& rdata) +ResourceRecord& DnsPacket::addRR(Dines::RecordSection section, const std::string& rrDomain, + unsigned rrType, unsigned rrClass, unsigned ttl, const std::string& rdata) { ResourceRecord rr(rrDomain, rrType, rrClass, ttl, rdata); return addRR(section, rr); @@ -410,6 +422,13 @@ void DnsPacket::txid(uint16_t txid) void DnsPacket::nRecord(Dines::RecordSection section, uint16_t value) { + char sect[10]; + char val[11]; + snprintf(sect, 10, "%u", section); + snprintf(val, 11, "%u", value); + if (_log) + _log("Setting record section " + string(sect) + " to " + string(val)); + _dnsHdr.nRecord(section, value); } @@ -487,3 +506,36 @@ void DnsPacket::setLogger(Dines::LogFunc l) { _log = l; } + +uint32_t DnsPacket::_convertIP(string rdata) +{ + uint32_t addr; + if (inet_pton(AF_INET, rdata.data(), &addr) != 1) + throw runtime_error("Can't convert IP address: " + rdata); + return addr; +} + +void DnsPacket::rDataIp() +{ + if (_log) + _log("Activating IP conversion"); + + _rDataIp = true; + + uint32_t addr; + for (vector::iterator itr = _answers.begin(); itr != _answers.end(); + ++itr) { + addr = _convertIP(itr->rData()); + itr->rData(string((char*)&addr, 4)); + } + for (vector::iterator itr = _authorities.begin(); itr != _authorities.end(); + ++itr) { + addr = _convertIP(itr->rData()); + itr->rData(string((char*)&addr, 4)); + } + for (vector::iterator itr = _additionals.begin(); itr != _additionals.end(); + ++itr) { + addr = _convertIP(itr->rData()); + itr->rData(string((char*)&addr, 4)); + } +} diff --git a/dns_packet.hpp b/dns_packet.hpp index be6b4dd..6e29ff7 100644 --- a/dns_packet.hpp +++ b/dns_packet.hpp @@ -42,6 +42,10 @@ class DnsPacket { //! DNS additionals std::vector _additionals; + uint32_t _convertIP(std::string rdata); + + bool _rDataIp; + bool _fuzzSrcIp; bool _fuzzSport; @@ -131,6 +135,10 @@ class DnsPacket { ResourceRecord& addRR(Dines::RecordSection section, const ResourceRecord& rr); + // Converts existing resource record rdata into IP addresses. Sets also a flag + // for future conversions + void rDataIp(); + void fuzz(); void fuzzSrcIp(); diff --git a/main.cpp b/main.cpp index 4b9792f..4445234 100644 --- a/main.cpp +++ b/main.cpp @@ -31,6 +31,7 @@ struct option opts[] = { {"auth", 1, NULL, 10}, {"num-add", 1, NULL, 11}, {"additional", 1, NULL, 12}, + {"rdata-ip", 0, NULL, 13}, // some space here for new params {"num", 1, NULL, 30}, {"delay", 1, NULL, 31}, @@ -65,10 +66,11 @@ void usage(string s) cout << "\n"; cout << "--num-add : number of additional records (AF)\n"; cout << "--additional(R) ,,,,: a DNS additional record\n"; + cout << "\n"; + cout << "--rdata-ip: use rdata as it was an IP address\n"; cout << "\n[MISC]\n"; cout << "--num : number of packets (0 = infinite)\n"; cout << "--delay : delay between packets\n"; - cout << "--debug: activate debug\n"; cout << "--verbose: be verbose\n"; cout << "--help: this help\n"; cout << "\n"; @@ -102,18 +104,6 @@ int main(int argc, char* argv[]) exit(1); } - // Scan cmd line to dig for options and activate them immediately -// for (int i = 0; i < argc; i++) { -// if (string(argv[i]) == "--debug") { -// cout << "Activating DEBUG\n"; -// theLog = new ostream(cout.rdbuf()); -// } -// if (string(argv[i]) == "--help") { -// usage(argv[0]); -// return 1; -// } -// } - if (getuid() != 0) { cout << "You need to be root." << endl; return 1; @@ -124,152 +114,183 @@ int main(int argc, char* argv[]) vector tokens; - while((c = getopt_long(argc, argv, "", opts, NULL)) != -1) { - switch(c) { - case 0: - if (optarg[0] == 'F') { - p.fuzzSrcIp(); - } else { - p.ipFrom(optarg); - } - break; - - case 1: - p.ipTo(optarg); - break; - - case 2: - if (optarg[0] == 'F') { - p.fuzzSport(); - } else { - p.sport(optarg); - } - break; - - case 3: - p.dport(optarg); - break; - - case 4: - if (optarg[0] == 'F') { - DnsHeader& h = p.dnsHdr(); - h.fuzzTxid(); - } else { - p.txid(optarg); - } - break; - - case 5: - if (optarg[0] == 'F') { - DnsHeader& h = p.dnsHdr(); - h.fuzzNRecord(Dines::R_QUESTION); - } else { - forged_nrecords[Dines::R_QUESTION] = atoi(optarg); - } - break; - - case 6: - tokens.clear(); - tokens = tokenize(optarg, ","); - - if (tokens.size() != 3) { - cout << "Syntax: --question ,,\n"; - return 1; - } - p.addQuestion(tokens.at(0), tokens.at(1), tokens.at(2)); - if (tokens.at(1).at(0) == 'F') { - DnsQuestion& q = p.question(); - q.fuzzQtype(); - } - if (tokens.at(2).at(0) == 'F') { - DnsQuestion& q = p.question(); - q.fuzzQclass(); - } - break; - - case 7: - if (optarg[0] == 'F') { - DnsHeader& h = p.dnsHdr(); - h.fuzzNRecord(Dines::R_ANSWER); - } else { - forged_nrecords[Dines::R_ANSWER] = atoi(optarg); - } - break; - - case 8: - tokens.clear(); - tokens = tokenize(optarg, ","); - - p.isQuestion(false); - - { - ResourceRecord& rr = p.addRR(Dines::R_ANSWER, tokens.at(0), - tokens.at(1), tokens.at(2), tokens.at(3), tokens.at(4)); + try { + while((c = getopt_long(argc, argv, "", opts, NULL)) != -1) { + switch(c) { + case 0: + if (optarg[0] == 'F') { + p.fuzzSrcIp(); + } else { + p.ipFrom(optarg); + } + break; + + case 1: + p.ipTo(optarg); + break; + + case 2: + if (optarg[0] == 'F') { + p.fuzzSport(); + } else { + p.sport(optarg); + } + break; + + case 3: + p.dport(optarg); + break; + + case 4: + if (optarg[0] == 'F') { + DnsHeader& h = p.dnsHdr(); + h.fuzzTxid(); + } else { + p.txid(optarg); + } + break; + + case 5: + if (optarg[0] == 'F') { + DnsHeader& h = p.dnsHdr(); + h.fuzzNRecord(Dines::R_QUESTION); + } else { + forged_nrecords[Dines::R_QUESTION] = atoi(optarg); + } + break; + + case 6: + tokens.clear(); + tokens = tokenize(optarg, ","); + + if (tokens.size() != 3) { + cout << "Syntax: --question ,,\n"; + return 1; + } + p.addQuestion(tokens.at(0), tokens.at(1), tokens.at(2)); if (tokens.at(1).at(0) == 'F') { - rr.fuzzRRtype(); + DnsQuestion& q = p.question(); + q.fuzzQtype(); } if (tokens.at(2).at(0) == 'F') { - rr.fuzzRRclass(); + DnsQuestion& q = p.question(); + q.fuzzQclass(); + } + break; + + case 7: + if (optarg[0] == 'F') { + DnsHeader& h = p.dnsHdr(); + h.fuzzNRecord(Dines::R_ANSWER); + } else { + forged_nrecords[Dines::R_ANSWER] = atoi(optarg); + } + break; + + case 8: + tokens.clear(); + tokens = tokenize(optarg, ","); + + { + ResourceRecord& rr = p.addRR(Dines::R_ANSWER, tokens.at(0), + tokens.at(1), tokens.at(2), tokens.at(3), tokens.at(4)); + + if (tokens.at(1).at(0) == 'F') { + rr.fuzzRRtype(); + } + if (tokens.at(2).at(0) == 'F') { + rr.fuzzRRclass(); + } + if (tokens.at(3).at(0) == 'F') { + rr.fuzzRRttl(); + } } - if (tokens.at(3).at(0) == 'F') { - rr.fuzzRRttl(); + break; + + case 9: + if (optarg[0] == 'F') { + DnsHeader& h = p.dnsHdr(); + h.fuzzNRecord(Dines::R_ADDITIONAL); + } else { + forged_nrecords[Dines::R_ADDITIONAL] = atoi(optarg); } - } - break; - - case 9: - if (optarg[0] == 'F') { - DnsHeader& h = p.dnsHdr(); - h.fuzzNRecord(Dines::R_ADDITIONAL); - } else { - forged_nrecords[Dines::R_ADDITIONAL] = atoi(optarg); - } - break; - - case 10: - tokens.clear(); - tokens = tokenize(optarg, ","); - - p.isQuestion(false); - p.addRR(Dines::R_AUTHORITIES, tokens.at(0), tokens.at(1), tokens.at(2), - tokens.at(3), tokens.at(4)); - break; - - case 11: - if (optarg[0] == 'F') { - DnsHeader& h = p.dnsHdr(); - h.fuzzNRecord(Dines::R_AUTHORITIES); - } else { - forged_nrecords[Dines::R_AUTHORITIES] = atoi(optarg); - } - break; - - case 12: - tokens.clear(); - tokens = tokenize(optarg, ","); - - p.addRR(Dines::R_ADDITIONAL, tokens.at(0), tokens.at(1), tokens.at(2), - tokens.at(3), tokens.at(4)); - break; - - case 30: - num = atoi(optarg); - break; - - case 31: - delay = atoi(optarg); - logger("Inter packet gap set to " + string(optarg)); - break; - - case 32: - logger("Verbose: ON"); - p.setLogger(logger); - verbose = true; - break; - default: - cout << "Unknown option.\n"; - return 1; + break; + + case 10: + tokens.clear(); + tokens = tokenize(optarg, ","); + + { + ResourceRecord& rr = p.addRR(Dines::R_AUTHORITIES, + tokens.at(0), tokens.at(1), tokens.at(2), + tokens.at(3), tokens.at(4)); + if (tokens.at(1).at(0) == 'F') { + rr.fuzzRRtype(); + } + if (tokens.at(2).at(0) == 'F') { + rr.fuzzRRclass(); + } + if (tokens.at(3).at(0) == 'F') { + rr.fuzzRRttl(); + } + } + break; + + case 11: + if (optarg[0] == 'F') { + DnsHeader& h = p.dnsHdr(); + h.fuzzNRecord(Dines::R_AUTHORITIES); + } else { + forged_nrecords[Dines::R_AUTHORITIES] = atoi(optarg); + } + break; + + case 12: + tokens.clear(); + tokens = tokenize(optarg, ","); + + { + ResourceRecord& rr = p.addRR(Dines::R_ADDITIONAL, + tokens.at(0), tokens.at(1), tokens.at(2), + tokens.at(3), tokens.at(4)); + if (tokens.at(1).at(0) == 'F') { + rr.fuzzRRtype(); + } + if (tokens.at(2).at(0) == 'F') { + rr.fuzzRRclass(); + } + if (tokens.at(3).at(0) == 'F') { + rr.fuzzRRttl(); + } + } + break; + + case 13: + p.rDataIp(); + break; + + case 30: + num = atoi(optarg); + break; + + case 31: + delay = atoi(optarg); + logger("Inter packet gap set to " + string(optarg)); + break; + + case 32: + logger("Verbose mode on"); + p.setLogger(logger); + verbose = true; + break; + default: + cout << "Unknown option: " << optarg << endl; + return 1; + } } + } catch(exception& e) { + cerr << "Invalid parameter: " << e.what() << endl; + return 2; } for (unsigned i = 0; i < 4; i++) { diff --git a/rr.cpp b/rr.cpp index e4d47e4..c1301b4 100644 --- a/rr.cpp +++ b/rr.cpp @@ -169,3 +169,8 @@ string ResourceRecord::to_string() const return _rrDomain_str + "/" + rrTypeStr() + "/" + rrClassStr() + "/" + ttlStr(); } + +void ResourceRecord::rData(string rdata) +{ + _rData = rdata; +} diff --git a/rr.hpp b/rr.hpp index 2233c7e..a5e3da8 100644 --- a/rr.hpp +++ b/rr.hpp @@ -46,6 +46,7 @@ class ResourceRecord { void ttl(std::string ttl); void ttl(unsigned ttl); + void rData(std::string rdata); std::string rData() const; unsigned rDataLen() const; diff --git a/test.cpp b/test.cpp index aab4027..5d99820 100644 --- a/test.cpp +++ b/test.cpp @@ -376,6 +376,12 @@ int test_conversion() return 0; } +int test_ip_conversion() +{ + // XXX + return 0; +} + int main(int argc, char* argv[]) { cout << "Tests running"; @@ -393,5 +399,6 @@ int main(int argc, char* argv[]) TEST(test_fuzz_packet()); TEST(test_invalid_section()); TEST(test_conversion()); + TEST(test_ip_conversion()); cout << "done" << endl; }