Difference between revisions of "Wireshark TLS"
(2 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
+ | tshark -l -i any -f 'port 443' -Y "ssl.handshake.ciphersuites" -Vx | ||
+ | |||
+ | tshark -i wlan1mon -l -T fields -e wlan.bssid -e wlan.ssid -e radiotap.dbm_antsignal -Y 'wlan.ssid' | grep -v "ff:ff:ff" | ||
+ | |||
+ | Maybe -e wlan.rsn.pcs.type and -e wlan.rsn.gcs.type are what you're looking for? Those are for the Pairwise Cipher Suite type" and "Group Cipher Suite type", respectively. | ||
+ | https://stackoverflow.com/questions/47645051/display-network-encryption-type-in-tshark | ||
+ | |||
+ | |||
+ | https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4 | ||
+ | |||
+ | |||
``` | ``` | ||
Client Hello: | Client Hello: | ||
Line 47: | Line 58: | ||
20 Finished | 20 Finished | ||
``` | ``` | ||
+ | |||
+ | |||
+ | {code} | ||
+ | #include <tins/tins.h> | ||
+ | |||
+ | #include <algorithm> | ||
+ | #include <iostream> | ||
+ | #include <map> | ||
+ | #include <string> | ||
+ | |||
+ | using namespace Tins; | ||
+ | using namespace std; | ||
+ | |||
+ | |||
+ | /* | ||
+ | * Container class for the data that is retrieved from the beacon. | ||
+ | */ | ||
+ | class Unit { | ||
+ | public: | ||
+ | /* | ||
+ | * Constructor. Parses the Dot11Beacon object and takes all the necessary | ||
+ | * data from it. | ||
+ | */ | ||
+ | Unit(Dot11Beacon& beacon); | ||
+ | Unit() = default; | ||
+ | unsigned getCount(); | ||
+ | void incrementCount(); | ||
+ | |||
+ | /* | ||
+ | * Prints this object onto the command line, in CSV format | ||
+ | */ | ||
+ | void print(); | ||
+ | |||
+ | private: | ||
+ | string ssid; | ||
+ | string bssid; | ||
+ | unsigned channel; | ||
+ | unsigned count; | ||
+ | |||
+ | string gcs; // Group Cipher Suite | ||
+ | string pcs; // Pairwise Cipher Suite | ||
+ | string akm; // Authentication suite | ||
+ | |||
+ | /* | ||
+ | * Returns a string representation of a RSNInformation::CypherSuites enum value | ||
+ | */ | ||
+ | string type_to_string(const RSNInformation::CypherSuites& type); | ||
+ | |||
+ | /* | ||
+ | * Returns a string representation of a RSNInformation::AKMSuites enum value | ||
+ | */ | ||
+ | string type_to_string(const RSNInformation::AKMSuites& type); | ||
+ | }; | ||
+ | |||
+ | Unit::Unit(Dot11Beacon& beacon) : | ||
+ | count {1} /* When this unit is created, it has been seen exactly once */ { | ||
+ | ssid = beacon.ssid(); | ||
+ | bssid = beacon.addr3().to_string(); | ||
+ | channel = unsigned(beacon.ds_parameter_set()); | ||
+ | |||
+ | RSNInformation rsn; | ||
+ | for(const auto &opt : beacon.options()) { | ||
+ | if (opt.option() == Dot11::RSN) { | ||
+ | rsn = beacon.rsn_information(); | ||
+ | |||
+ | // Put all authentication suite types in a string | ||
+ | const RSNInformation::akm_type& akmTypeList = rsn.akm_cyphers(); | ||
+ | for (const auto& akmIt : akmTypeList) { | ||
+ | if (akm.size() == 0) | ||
+ | akm += type_to_string(akmIt); | ||
+ | else | ||
+ | akm += ";" + type_to_string(akmIt); | ||
+ | } | ||
+ | |||
+ | // Put all group cipher types in a string | ||
+ | const RSNInformation::CypherSuites& gcsType = rsn.group_suite(); | ||
+ | gcs = type_to_string(gcsType); | ||
+ | |||
+ | |||
+ | // Put all pairwise ciphers in a string | ||
+ | const RSNInformation::cyphers_type& pcsTypeList = rsn.pairwise_cyphers(); | ||
+ | for (const auto& pcsIt : pcsTypeList) { | ||
+ | if (pcs.size() == 0) | ||
+ | pcs += type_to_string(pcsIt); | ||
+ | else | ||
+ | pcs += ";" + type_to_string(pcsIt); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | unsigned Unit::getCount() { | ||
+ | return count; | ||
+ | } | ||
+ | |||
+ | void Unit::incrementCount() { | ||
+ | count += 1; | ||
+ | } | ||
+ | |||
+ | void Unit::print() { | ||
+ | string ssid_to_print; | ||
+ | if (ssid.length() == 0) { | ||
+ | ssid_to_print = "<ZERO_LENGTH>"; | ||
+ | } else if (!isprint(ssid[0])) { | ||
+ | ssid_to_print = to_string(static_cast<int>(ssid[0])); | ||
+ | } else { | ||
+ | ssid_to_print = ssid; | ||
+ | } | ||
+ | if (find(ssid_to_print.begin(), ssid_to_print.end(), ',') != ssid_to_print.end()) { | ||
+ | ssid_to_print = "\"" + ssid_to_print + "\""; | ||
+ | } | ||
+ | cout << ssid_to_print << "," | ||
+ | << bssid << "," | ||
+ | << to_string(channel) << "," | ||
+ | << to_string(count) << "," | ||
+ | << gcs << "," | ||
+ | << pcs << "," | ||
+ | << akm << endl; | ||
+ | } | ||
+ | |||
+ | string Unit::type_to_string(const RSNInformation::CypherSuites& type) { | ||
+ | switch (type) { | ||
+ | case RSNInformation::CypherSuites::CCMP: | ||
+ | return "CCMP"; | ||
+ | break; | ||
+ | case RSNInformation::CypherSuites::TKIP: | ||
+ | return "TKIP"; | ||
+ | break; | ||
+ | case RSNInformation::CypherSuites::WEP_104: | ||
+ | return "WEP_104"; | ||
+ | break; | ||
+ | case RSNInformation::CypherSuites::WEP_40: | ||
+ | return "WEP_40"; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | string Unit::type_to_string(const RSNInformation::AKMSuites& type) { | ||
+ | switch (type) { | ||
+ | case RSNInformation::AKMSuites::PMKSA: | ||
+ | return "PMKSA"; | ||
+ | break; | ||
+ | case RSNInformation::AKMSuites::PSK: | ||
+ | return "PSK"; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /* | ||
+ | * Class that reads the pcap, keeps track of the units and writes out one | ||
+ | * beacon frame in pcap format for each unique AP it finds. This file is called | ||
+ | * "unique_beacons.pcap" | ||
+ | */ | ||
+ | class PCAPParser { | ||
+ | public: | ||
+ | /* | ||
+ | * Constructor. It takes the exact parameters that it will pas on to its | ||
+ | * FileSniffer object (a FileSniffer is actually just a file reader). | ||
+ | */ | ||
+ | PCAPParser(const string& pcapFilename, const string& filter); | ||
+ | |||
+ | /* | ||
+ | * Start reading the file. | ||
+ | */ | ||
+ | bool run(); | ||
+ | |||
+ | /* | ||
+ | * Print CSV header and ask all of our collected Unit objects to print themselves | ||
+ | */ | ||
+ | void print(); | ||
+ | |||
+ | private: | ||
+ | FileSniffer sniffer; | ||
+ | PacketWriter writer; | ||
+ | map<string, Unit> apMap; // stands for Access Point Map | ||
+ | |||
+ | bool handler(PDU&); | ||
+ | }; | ||
+ | |||
+ | PCAPParser::PCAPParser(const string& pcapFilename, const string& filter) : | ||
+ | sniffer {pcapFilename, filter}, | ||
+ | writer {"unique_beacons.pcap", PacketWriter::RADIOTAP} { | ||
+ | for (auto it = apMap.begin(); it != apMap.end(); it++) { | ||
+ | it->second.print(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | bool PCAPParser::run() { | ||
+ | sniffer.sniff_loop( [this] (PDU& pdu) { return (bool) this->handler (pdu); } ); | ||
+ | return true; | ||
+ | } | ||
+ | |||
+ | bool PCAPParser::handler(PDU& pdu) { | ||
+ | Dot11Beacon& beacon = pdu.rfind_pdu<Dot11Beacon>(); | ||
+ | |||
+ | // An ESSID may span multiple BSSID's. Also, it's nice to keep track of what | ||
+ | // channels an AP has been on. Therefore, the combination of SSID, BSSID and | ||
+ | // channel is considered key. | ||
+ | const string& ssid = beacon.ssid(); | ||
+ | const string& mac = beacon.addr3().to_string(); | ||
+ | const unsigned channel = unsigned(beacon.ds_parameter_set()); | ||
+ | const string key = ssid + mac + to_string(channel); | ||
+ | if (apMap.find(key) == apMap.end()) { // we've got a new one | ||
+ | Unit unit(beacon); | ||
+ | apMap[key] = unit; | ||
+ | writer.write(pdu); | ||
+ | } else { | ||
+ | apMap[key].incrementCount(); | ||
+ | } | ||
+ | return true; | ||
+ | } | ||
+ | |||
+ | void PCAPParser::print() { | ||
+ | // Print the headers for the CSV output | ||
+ | cout << "SSID,BSSID,Current_channel,Count,Group_Cipher,Pairwise_Ciphers,Authentication_Suite" << endl; | ||
+ | |||
+ | // Ask each of the units to print themselves for the CSV output | ||
+ | for (auto it = apMap.begin(); it != apMap.end(); it++) { | ||
+ | it->second.print(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | int main(int argc, char *argv[]) { | ||
+ | if(argc != 2) { | ||
+ | std::cout << "Usage: " << *argv << " <PCAP_FILE>\n"; | ||
+ | return 1; | ||
+ | } | ||
+ | |||
+ | PCAPParser pcapParser(argv[1], "wlan type mgt subtype beacon"); | ||
+ | pcapParser.run(); | ||
+ | pcapParser.print(); | ||
+ | } | ||
+ | |||
+ | {code} |
Latest revision as of 03:20, 1 March 2022
tshark -l -i any -f 'port 443' -Y "ssl.handshake.ciphersuites" -Vx
tshark -i wlan1mon -l -T fields -e wlan.bssid -e wlan.ssid -e radiotap.dbm_antsignal -Y 'wlan.ssid' | grep -v "ff:ff:ff"
Maybe -e wlan.rsn.pcs.type and -e wlan.rsn.gcs.type are what you're looking for? Those are for the Pairwise Cipher Suite type" and "Group Cipher Suite type", respectively. https://stackoverflow.com/questions/47645051/display-network-encryption-type-in-tshark
https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4
Client Hello: ssl.handshake.type == 1 Server Hello: ssl.handshake.type == 2 NewSessionTicket: ssl.handshake.type == 4 Certificate: ssl.handshake.type == 11 CertificateRequest ssl.handshake.type == 13 ServerHelloDone: ssl.handshake.type == 14 Note: “ServerHellpDone” means full-handshake TLS session. Cipher Suites: ssl.handshake.ciphersuite TLS handshake message types Message types Code Description 0 HelloRequest 1 ClientHello 2 ServerHello 4 NewSessionTicket 8 EncryptedExtensions (TLS 1.3 only) 11 Certificate 12 ServerKeyExchange 13 CertificateRequest 14 ServerHelloDone 15 CertificateVerify 16 ClientKeyExchange 20 Finished
{code}
include <tins/tins.h>
include
include
include
include
using namespace Tins; using namespace std;
/*
* Container class for the data that is retrieved from the beacon. /
class Unit { public:
/ * Constructor. Parses the Dot11Beacon object and takes all the necessary * data from it. */ Unit(Dot11Beacon& beacon); Unit() = default; unsigned getCount(); void incrementCount();
/*
* Prints this object onto the command line, in CSV format
*/
void print();
private:
string ssid; string bssid; unsigned channel; unsigned count;
string gcs; // Group Cipher Suite
string pcs; // Pairwise Cipher Suite
string akm; // Authentication suite
/*
* Returns a string representation of a RSNInformation::CypherSuites enum value
*/
string type_to_string(const RSNInformation::CypherSuites& type);
/*
* Returns a string representation of a RSNInformation::AKMSuites enum value
*/
string type_to_string(const RSNInformation::AKMSuites& type);
};
Unit::Unit(Dot11Beacon& beacon) :
count {1} /* When this unit is created, it has been seen exactly once */ { ssid = beacon.ssid(); bssid = beacon.addr3().to_string(); channel = unsigned(beacon.ds_parameter_set());
RSNInformation rsn;
for(const auto &opt : beacon.options()) {
if (opt.option() == Dot11::RSN) {
rsn = beacon.rsn_information();
// Put all authentication suite types in a string
const RSNInformation::akm_type& akmTypeList = rsn.akm_cyphers();
for (const auto& akmIt : akmTypeList) {
if (akm.size() == 0)
akm += type_to_string(akmIt);
else
akm += ";" + type_to_string(akmIt);
}
// Put all group cipher types in a string
const RSNInformation::CypherSuites& gcsType = rsn.group_suite();
gcs = type_to_string(gcsType);
// Put all pairwise ciphers in a string
const RSNInformation::cyphers_type& pcsTypeList = rsn.pairwise_cyphers();
for (const auto& pcsIt : pcsTypeList) {
if (pcs.size() == 0)
pcs += type_to_string(pcsIt);
else
pcs += ";" + type_to_string(pcsIt);
}
}
}
}
unsigned Unit::getCount() {
return count;
}
void Unit::incrementCount() {
count += 1;
}
void Unit::print() {
string ssid_to_print; if (ssid.length() == 0) { ssid_to_print = ""; } else if (!isprint(ssid[0])) { ssid_to_print = to_string(static_cast (ssid[0])); } else { ssid_to_print = ssid; } if (find(ssid_to_print.begin(), ssid_to_print.end(), ',') != ssid_to_print.end()) { ssid_to_print = "\"" + ssid_to_print + "\""; } cout << ssid_to_print << "," << bssid << "," << to_string(channel) << "," << to_string(count) << "," << gcs << "," << pcs << "," << akm << endl;
}
string Unit::type_to_string(const RSNInformation::CypherSuites& type) {
switch (type) { case RSNInformation::CypherSuites::CCMP: return "CCMP"; break; case RSNInformation::CypherSuites::TKIP: return "TKIP"; break; case RSNInformation::CypherSuites::WEP_104: return "WEP_104"; break; case RSNInformation::CypherSuites::WEP_40: return "WEP_40"; break; }
}
string Unit::type_to_string(const RSNInformation::AKMSuites& type) {
switch (type) { case RSNInformation::AKMSuites::PMKSA: return "PMKSA"; break; case RSNInformation::AKMSuites::PSK: return "PSK"; break; }
}
/*
* Class that reads the pcap, keeps track of the units and writes out one * beacon frame in pcap format for each unique AP it finds. This file is called * "unique_beacons.pcap" /
class PCAPParser { public:
/ * Constructor. It takes the exact parameters that it will pas on to its * FileSniffer object (a FileSniffer is actually just a file reader). */ PCAPParser(const string& pcapFilename, const string& filter);
/*
* Start reading the file.
*/
bool run();
/*
* Print CSV header and ask all of our collected Unit objects to print themselves
*/
void print();
private:
FileSniffer sniffer; PacketWriter writer; map<string, Unit> apMap; // stands for Access Point Map
bool handler(PDU&);
};
PCAPParser::PCAPParser(const string& pcapFilename, const string& filter) :
sniffer {pcapFilename, filter}, writer {"unique_beacons.pcap", PacketWriter::RADIOTAP} { for (auto it = apMap.begin(); it != apMap.end(); it++) { it->second.print(); }
}
bool PCAPParser::run() {
sniffer.sniff_loop( [this] (PDU& pdu) { return (bool) this->handler (pdu); } ); return true;
}
bool PCAPParser::handler(PDU& pdu) {
Dot11Beacon& beacon = pdu.rfind_pdu();
// An ESSID may span multiple BSSID's. Also, it's nice to keep track of what
// channels an AP has been on. Therefore, the combination of SSID, BSSID and
// channel is considered key.
const string& ssid = beacon.ssid();
const string& mac = beacon.addr3().to_string();
const unsigned channel = unsigned(beacon.ds_parameter_set());
const string key = ssid + mac + to_string(channel);
if (apMap.find(key) == apMap.end()) { // we've got a new one
Unit unit(beacon);
apMap[key] = unit;
writer.write(pdu);
} else {
apMap[key].incrementCount();
}
return true;
}
void PCAPParser::print() {
// Print the headers for the CSV output cout << "SSID,BSSID,Current_channel,Count,Group_Cipher,Pairwise_Ciphers,Authentication_Suite" << endl;
// Ask each of the units to print themselves for the CSV output
for (auto it = apMap.begin(); it != apMap.end(); it++) {
it->second.print();
}
}
int main(int argc, char *argv[]) {
if(argc != 2) { std::cout << "Usage: " << *argv << "\n"; return 1; }
PCAPParser pcapParser(argv[1], "wlan type mgt subtype beacon");
pcapParser.run();
pcapParser.print();
}
{code}