1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-27 12:14:26 +01:00

Added WPA2Decrypter class.

This commit is contained in:
Matias Fontanini
2013-05-30 23:36:14 -03:00
parent ddf47365c1
commit 908fcb56e8
8 changed files with 552 additions and 29 deletions

View File

@@ -100,4 +100,5 @@ libtins_include_HEADERS = include/internals.h \
include/utils.h \
include/cxxstd.h \
include/stp.h \
include/exceptions.h
include/exceptions.h \
include/config.h

View File

@@ -384,7 +384,8 @@ libtins_include_HEADERS = include/internals.h \
include/utils.h \
include/cxxstd.h \
include/stp.h \
include/exceptions.h
include/exceptions.h \
include/config.h
all: all-am

84
configure vendored
View File

@@ -775,6 +775,7 @@ enable_maintainer_mode
with_pcap_include_path
with_pcap_lib_path
enable_c__11
enable_wpa2
'
ac_precious_vars='build_alias
host_alias
@@ -1418,6 +1419,7 @@ Optional Features:
--disable-maintainer-mode disable make rules and dependencies not useful
(and sometimes confusing) to the casual installer
--enable-c++11 enable C++11 features
--disable-wpa2 disable WPA2 decryption features
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -15199,6 +15201,8 @@ CPPFLAGS=$old_cppflags
# Options
wpa2_msg="WPA2 decryption(which requires openssl) can be disabled using the --disable-wpa2 flag."
# Check whether --enable-c++11 was given.
if test "${enable_c__11+set}" = set; then :
enableval=$enable_c__11; ax_cxx_compile_cxx11_required=truednl
@@ -15324,6 +15328,86 @@ $as_echo "#define HAVE_CXX11 1" >>confdefs.h
fi
fi
# Check whether --enable-wpa2 was given.
if test "${enable_wpa2+set}" = set; then :
enableval=$enable_wpa2;
else
for ac_header in openssl/evp.h openssl/hmac.h openssl/aes.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
else
as_fn_error $? "openssl headers are missing! $wpa2_msg " "$LINENO" 5
$as_echo "#define HAVE_WPA2_DECRYPTION 0" >>confdefs.h
fi
done
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for PKCS5_PBKDF2_HMAC_SHA1 in -lcrypto" >&5
$as_echo_n "checking for PKCS5_PBKDF2_HMAC_SHA1 in -lcrypto... " >&6; }
if ${ac_cv_lib_crypto_PKCS5_PBKDF2_HMAC_SHA1+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lcrypto $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char PKCS5_PBKDF2_HMAC_SHA1 ();
int
main ()
{
return PKCS5_PBKDF2_HMAC_SHA1 ();
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_link "$LINENO"; then :
ac_cv_lib_crypto_PKCS5_PBKDF2_HMAC_SHA1=yes
else
ac_cv_lib_crypto_PKCS5_PBKDF2_HMAC_SHA1=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_PKCS5_PBKDF2_HMAC_SHA1" >&5
$as_echo "$ac_cv_lib_crypto_PKCS5_PBKDF2_HMAC_SHA1" >&6; }
if test "x$ac_cv_lib_crypto_PKCS5_PBKDF2_HMAC_SHA1" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBCRYPTO 1
_ACEOF
LIBS="-lcrypto $LIBS"
else
as_fn_error $? "openssl library is missing! $wpa2_msg " "$LINENO" 5
fi
$as_echo "#define HAVE_WPA2_DECRYPTION 1" >>confdefs.h
fi

View File

@@ -36,12 +36,35 @@ CPPFLAGS=$old_cppflags
# Options
wpa2_msg="WPA2 decryption(which requires openssl) can be disabled using the --disable-wpa2 flag."
AC_ARG_ENABLE(
c++11,
[ --enable-c++11 enable C++11 features],
[AX_CXX_COMPILE_STDCXX_11(noext)]
)
AC_ARG_ENABLE(
wpa2,
[ --disable-wpa2 disable WPA2 decryption features],
[],
[
AC_CHECK_HEADERS(
[openssl/evp.h openssl/hmac.h openssl/aes.h],
[],
[AC_MSG_ERROR([openssl headers are missing! $wpa2_msg ])]
AC_DEFINE([HAVE_WPA2_DECRYPTION], 0, Have WPA2 decryption library)
)
AC_CHECK_LIB(
crypto,
PKCS5_PBKDF2_HMAC_SHA1,
[],
[AC_MSG_ERROR([openssl library is missing! $wpa2_msg ])]
)
AC_DEFINE([HAVE_WPA2_DECRYPTION], 1, Have WPA2 decryption library)
]
)
# Substitute options
AC_SUBST(CXXFLAGS)

View File

@@ -9,12 +9,24 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `crypto' library (-lcrypto). */
#undef HAVE_LIBCRYPTO
/* Define to 1 if you have the `pcap' library (-lpcap). */
#undef HAVE_LIBPCAP
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <openssl/aes.h> header file. */
#undef HAVE_OPENSSL_AES_H
/* Define to 1 if you have the <openssl/evp.h> header file. */
#undef HAVE_OPENSSL_EVP_H
/* Define to 1 if you have the <openssl/hmac.h> header file. */
#undef HAVE_OPENSSL_HMAC_H
/* Define to 1 if you have the <pcap.h> header file. */
#undef HAVE_PCAP_H
@@ -39,6 +51,9 @@
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Have WPA2 decryption library */
#undef HAVE_WPA2_DECRYPTION
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR

View File

@@ -34,15 +34,57 @@
#include <string>
#include <algorithm>
#include <vector>
#include "dot11.h"
#include "utils.h"
#include "snap.h"
#include "rawpdu.h"
#include "handshake_capturer.h"
#include "config.h"
namespace Tins {
class PDU;
class Dot11;
class Dot11Data;
namespace Crypto {
/**
* \cond
*/
#ifdef HAVE_WPA2_DECRYPTION
namespace WPA2 {
class invalid_handshake : public std::exception {
public:
const char *what() const throw() {
return "invalid handshake";
}
};
class CCMPSessionKeys {
public:
typedef Internals::byte_array<80> ptk_type;
typedef Internals::byte_array<32> pmk_type;
CCMPSessionKeys(const RSNHandshake &hs, const pmk_type &pmk);
SNAP *decrypt_unicast(const Dot11Data &dot11, const RawPDU &raw) const;
private:
ptk_type ptk;
};
class SupplicantData {
public:
typedef HWAddress<6> address_type;
typedef CCMPSessionKeys::pmk_type pmk_type;
SupplicantData(const std::string &psk, const std::string &ssid);
const pmk_type &pmk() const;
private:
pmk_type pmk_;
};
}
#endif // HAVE_WPA2_DECRYPTION
/**
* \endcond
*/
/**
* \brief RC4 Key abstraction.
*/
@@ -65,11 +107,11 @@ namespace Crypto {
};
/**
*
* \brief Decrypts WEP-encrypted traffic.
*/
class WEPDecrypter {
public:
typedef Dot11::address_type address_type;
typedef HWAddress<6> address_type;
/**
* \brief Constructs a WEPDecrypter object.
@@ -93,19 +135,18 @@ namespace Crypto {
void remove_password(const address_type &addr);
/**
* \brief Decrypts the provided PDU and forwards the decrypted
* PDU to the functor held by this object.
* \brief Decrypts the provided PDU.
*
* A Dot11Data PDU is looked up inside the provided PDU chain.
* If no such PDU exists or there is no password associated
* with the Dot11 packet's BSSID, then the PDU is left intact.
*
* Otherwise, the packet is decrypted using the given password.
* If the CRC found after decrypting it is invalid,
* then false is returned.
* If the CRC found after decrypting is invalid, false is
* returned.
*
* \return false if decryption failed due to invalid CRC, true
* otherwise.
* \return false if no decryption was performed or decryption
* failed, true otherwise.
*/
bool decrypt(PDU &pdu);
private:
@@ -117,6 +158,95 @@ namespace Crypto {
std::vector<uint8_t> key_buffer;
};
#ifdef HAVE_WPA2_DECRYPTION
/**
* \brief Decrypts WPA2-encrypted traffic.
*
* This class takes valid PSK and SSID tuples, captures client handshakes,
* and decrypts their traffic afterwards.
*/
class WPA2Decrypter {
public:
/*
* \brief The type used to store Dot11 addresses.
*/
typedef HWAddress<6> address_type;
/**
* \brief Adds a supplicant's information.
*
* This associates an SSID with a PSK., and allows the decryption of
* any BSSIDs that broadcast the same SSID.
*
* The decrypter will inspect beacon frames, looking for SSID tags
* that contain the given SSID.
*
* Note that using this overload, the decryption of data frames and
* handshake capturing will be disabled until any access point
* broadcasts the provided SSID(this shouldn't take long at all).
* If this is not the desired behaviour, then you should check out
* the ovther add_supplicant_data overload.
*
* \param psk The PSK associated with the SSID.
* \param ssid The network's SSID.
*/
void add_supplicant_data(const std::string &psk, const std::string &ssid);
/**
* \brief Adds a supplicant's information, including the BSSID.
*
* This overload can be used if the BSSID associated with this SSID is
* known beforehand. The addr parameter indicates which specific BSSID
* is associated to the SSID.
*
* Note that if any other access point broadcasts the provided SSID,
* it will be taken into account as well.
*
* \param psk The PSK associated with this SSID.
* \param ssid The network's SSID.
* \param addr The access point's BSSID.
*/
void add_supplicant_data(const std::string &psk, const std::string &ssid, const address_type &addr);
/**
* \brief Decrypts the provided PDU.
*
* A Dot11Data PDU is looked up inside the provided PDU chain.
* If no such PDU exists or no PSK was associated with the SSID
* broadcasted by the Dot11 packet's BSSID, or no EAPOL handshake
* was captured for the client involved in the communication,
* then the PDU is left intact.
*
* Otherwise, the packet is decrypted using the generated PTK.
* If the resulting MIC is invalid, then the packet is left intact.
*
* \return false if no decryption was performed, or the decryption
* failed, true otherwise.
*/
bool decrypt(PDU &pdu);
private:
typedef std::map<std::string, WPA2::SupplicantData> pmks_map;
typedef std::map<address_type, WPA2::SupplicantData> bssids_map;
typedef std::pair<address_type, address_type> addr_pair;
typedef std::map<addr_pair, WPA2::CCMPSessionKeys> keys_map;
void try_add_keys(const Dot11Data &dot11, const RSNHandshake &hs);
addr_pair make_addr_pair(const address_type &addr1, const address_type &addr2) {
return (addr1 < addr2) ?
std::make_pair(addr1, addr2) :
std::make_pair(addr2, addr1);
}
addr_pair extract_addr_pair(const Dot11Data &dot11);
bssids_map::const_iterator find_ap(const Dot11Data &dot11);
void add_access_point(const std::string &ssid, const address_type &addr);
RSNHandshakeCapturer capturer;
pmks_map pmks;
bssids_map aps;
keys_map keys;
};
#endif // HAVE_WPA2_DECRYPTION
/**
* \brief Pluggable decrypter object which can be used to decrypt
* data on sniffing sessions.

View File

@@ -41,27 +41,70 @@
*/
namespace Tins {
namespace Internals {
void skip_line(std::istream &input);
bool from_hex(const std::string &str, uint32_t &result);
template<size_t n>
class byte_array {
public:
typedef uint8_t* iterator;
typedef const uint8_t* const_iterator;
template<bool, typename>
struct enable_if {
};
byte_array() {
std::fill(begin(), end(), 0);
}
template<typename InputIterator>
byte_array(InputIterator start, InputIterator last) {
std::copy(start, end, data);
}
template<typename InputIterator>
byte_array(InputIterator start) {
std::copy(start, n, data);
}
iterator begin() {
return data;
}
iterator end() {
return data + n;
}
const_iterator begin() const {
return data;
}
const_iterator end() const {
return data + n;
}
size_t size() const {
return n;
}
private:
uint8_t data[n];
};
template<typename T>
struct enable_if<true, T> {
typedef T type;
};
void skip_line(std::istream &input);
bool from_hex(const std::string &str, uint32_t &result);
template<bool, typename>
struct enable_if {
PDU *pdu_from_flag(Constants::Ethernet::e flag, const uint8_t *buffer,
uint32_t size, bool rawpdu_on_no_match = true);
PDU *pdu_from_flag(PDU::PDUType type, const uint8_t *buffer, uint32_t size);
Constants::Ethernet::e pdu_flag_to_ether_type(PDU::PDUType flag);
}
}
};
template<typename T>
struct enable_if<true, T> {
typedef T type;
};
PDU *pdu_from_flag(Constants::Ethernet::e flag, const uint8_t *buffer,
uint32_t size, bool rawpdu_on_no_match = true);
PDU *pdu_from_flag(PDU::PDUType type, const uint8_t *buffer, uint32_t size);
Constants::Ethernet::e pdu_flag_to_ether_type(PDU::PDUType flag);
} // namespace Internals
} // namespace Tins
/**
* \endcond
*/

View File

@@ -27,7 +27,11 @@
*
*/
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/aes.h>
#include "crypto.h"
#include "dot11.h"
namespace Tins {
namespace Crypto {
@@ -98,5 +102,227 @@ PDU *WEPDecrypter::decrypt(RawPDU &raw, const std::string &password) {
return 0;
}
}
#ifdef HAVE_WPA2_DECRYPTION
// WPA2Decrypter
const HWAddress<6> &min(const HWAddress<6>& lhs, const HWAddress<6>& rhs) {
return lhs < rhs ? lhs : rhs;
}
const HWAddress<6> &max(const HWAddress<6>& lhs, const HWAddress<6>& rhs) {
return lhs < rhs ? rhs : lhs;
}
template<typename InputIterator1, typename InputIterator2, typename OutputIterator>
void xor_range(InputIterator1 src1, InputIterator2 src2, OutputIterator dst, size_t sz) {
for(size_t i = 0; i < sz; ++i) {
*dst++ = *src1++ ^ *src2++;
}
}
namespace WPA2 {
CCMPSessionKeys::CCMPSessionKeys(const RSNHandshake &hs, const pmk_type &pmk) {
uint8_t PKE[100] = "Pairwise key expansion";
uint8_t MIC[16];
min(hs.client_address(), hs.supplicant_address()).copy(PKE + 23);
max(hs.client_address(), hs.supplicant_address()).copy(PKE + 29);
const uint8_t *nonce1 = hs.handshake()[1].nonce(),
*nonce2 = hs.handshake()[2].nonce();
if(std::lexicographical_compare(nonce1, nonce1 + 32, nonce2, nonce2 + 32)) {
std::copy(nonce1, nonce1 + 32, PKE + 35);
std::copy(nonce2, nonce2 + 32, PKE + 67);
}
else {
std::copy(nonce2, nonce2 + 32, PKE + 35);
std::copy(nonce1, nonce1 + 32, PKE + 67);
}
for(int i(0); i < 4; ++i) {
PKE[99] = i;
HMAC(EVP_sha1(), pmk.begin(), pmk.size(), PKE, 100, ptk.begin() + i * 20, 0);
}
PDU::serialization_type buffer = const_cast<RSNEAPOL&>(hs.handshake()[3]).serialize();
std::fill(buffer.begin() + 81, buffer.begin() + 81 + 16, 0);
if(hs.handshake()[3].key_descriptor() == 2)
HMAC(EVP_sha1(), ptk.begin(), 16, &buffer[0], buffer.size(), MIC, 0);
else
HMAC(EVP_md5(), ptk.begin(), 16, &buffer[0], buffer.size(), MIC, 0);
if(!std::equal(MIC, MIC + sizeof(MIC), hs.handshake()[3].mic()))
throw invalid_handshake();
}
SNAP *CCMPSessionKeys::decrypt_unicast(const Dot11Data &dot11, const RawPDU &raw) const {
const RawPDU::payload_type &pload = raw.payload();
uint8_t MIC[16] = {0};
uint8_t PN[6] = {
pload[7],
pload[6],
pload[5],
pload[4],
pload[1],
pload[0]
};
uint8_t AAD[32] = {0};
AAD[0] = 0;
AAD[1] = 22 + 6 * int(dot11.from_ds() && dot11.to_ds());
if(dot11.subtype() == Dot11::QOS_DATA_DATA)
AAD[1] += 2;
AAD[2] = dot11.protocol() | (dot11.type() << 2) | ((dot11.subtype() << 4) & 0x80);
AAD[3] = 0x40 | dot11.to_ds() | (dot11.from_ds() << 1) |
(dot11.more_frag() << 2) | (dot11.order() << 7);
dot11.addr1().copy(AAD + 4);
dot11.addr2().copy(AAD + 10);
dot11.addr3().copy(AAD + 16);
AAD[22] = dot11.frag_num();
AAD[23] = 0;
if(dot11.from_ds() && dot11.to_ds())
dot11.addr4().copy(AAD + 24);
AES_KEY ctx;
AES_set_encrypt_key(ptk.begin() + 32, 128, &ctx);
uint8_t crypted_block[16];
size_t total_sz = raw.payload_size() - 16, offset = 8, blocks = (total_sz + 15) / 16;
std::vector<uint8_t> output(total_sz);
uint8_t counter[16];
counter[0] = 0x59;
counter[1] = 0;
dot11.addr2().copy(counter + 2);
std::copy(PN, PN + 6, counter + 8);
counter[14] = (total_sz >> 8) & 0xff;
counter[15] = total_sz & 0xff;
AES_encrypt(counter, MIC, &ctx);
xor_range(MIC, AAD, MIC, 16);
AES_encrypt(MIC, MIC, &ctx);
xor_range(MIC, AAD + 16, MIC, 16);
AES_encrypt(MIC, MIC, &ctx);
counter[0] = 1;
counter[14] = counter[15] = 0;
AES_encrypt(counter, crypted_block, &ctx);
uint8_t nice_MIC[8];
std::copy(pload.begin() + pload.size() - 8, pload.end(), nice_MIC);
xor_range(crypted_block, nice_MIC, nice_MIC, 8);
for(size_t i = 1; i <= blocks; ++i) {
size_t block_sz = (i == blocks) ? (total_sz % 16) : 16;
counter[14] = (i >> 8) & 0xff;
counter[15] = i & 0xff;
AES_encrypt(counter, crypted_block, &ctx );
xor_range(crypted_block, &pload[offset], &output[(i - 1) * 16], block_sz);
xor_range(MIC, &output[(i - 1) * 16], MIC, block_sz);
AES_encrypt(MIC, MIC, &ctx);
offset += block_sz;
}
return (std::equal(nice_MIC, nice_MIC + sizeof(nice_MIC), MIC)) ?
new SNAP(&output[0], output.size()) :
0;
}
// supplicant_data
SupplicantData::SupplicantData(const std::string &psk, const std::string &ssid) {
PKCS5_PBKDF2_HMAC_SHA1(
psk.c_str(),
psk.size(),
(unsigned char *)ssid.c_str(),
ssid.size(),
4096,
pmk_.size(),
pmk_.begin()
);
}
const SupplicantData::pmk_type &SupplicantData::pmk() const {
return pmk_;
}
} // namespace WPA2
void WPA2Decrypter::add_supplicant_data(const std::string &psk, const std::string &ssid) {
pmks.insert(std::make_pair(ssid, WPA2::SupplicantData(psk, ssid)));
}
void WPA2Decrypter::add_supplicant_data(const std::string &psk, const std::string &ssid,
const address_type &addr)
{
// ADD CODE PLX
add_supplicant_data(psk, ssid);
}
void WPA2Decrypter::add_access_point(const std::string &ssid, const address_type &addr) {
pmks_map::const_iterator it = pmks.find(ssid);
if(it == pmks.end())
throw std::runtime_error("supplicant data not registered");
aps.insert(std::make_pair(addr, it->second));
}
void WPA2Decrypter::try_add_keys(const Dot11Data &dot11, const RSNHandshake &hs) {
bssids_map::const_iterator it = find_ap(dot11);
if(it != aps.end()) {
addr_pair addr_p = extract_addr_pair(dot11);
try {
keys.insert(std::make_pair(addr_p, WPA2::CCMPSessionKeys(hs, it->second.pmk())));
}
catch(WPA2::invalid_handshake&) { }
}
}
WPA2Decrypter::addr_pair WPA2Decrypter::extract_addr_pair(const Dot11Data &dot11) {
if(dot11.from_ds() && !dot11.to_ds())
return make_addr_pair(dot11.addr2(), dot11.addr3());
else if(!dot11.from_ds() && dot11.to_ds())
return make_addr_pair(dot11.addr1(), dot11.addr2());
else
return make_addr_pair(dot11.addr2(), dot11.addr3());
}
WPA2Decrypter::bssids_map::const_iterator WPA2Decrypter::find_ap(const Dot11Data &dot11) {
if(dot11.from_ds() && !dot11.to_ds())
return aps.find(dot11.addr2());
else if(!dot11.from_ds() && dot11.to_ds())
return aps.find(dot11.addr1());
else
return aps.find(dot11.addr3());
}
bool WPA2Decrypter::decrypt(PDU &pdu) {
if(capturer.process_packet(pdu)) {
try_add_keys(pdu.rfind_pdu<Dot11Data>(), capturer.handshakes().front());
capturer.clear_handshakes();
}
else if(const Dot11Beacon *beacon = pdu.find_pdu<Dot11Beacon>()) {
if(aps.count(beacon->addr3()) == 0) {
try {
std::string ssid = beacon->ssid();
if(pmks.count(ssid)) {
add_access_point(ssid, beacon->addr3());
}
}
catch(option_not_found&) { }
}
}
else {
Dot11Data *data = pdu.find_pdu<Dot11Data>();
RawPDU *raw = pdu.find_pdu<RawPDU>();
if(data && raw && data->wep()) {
keys_map::const_iterator it = keys.find(extract_addr_pair(*data));
if(it != keys.end()) {
SNAP *snap = it->second.decrypt_unicast(*data, *raw);
if(snap) {
data->inner_pdu(snap);
data->wep(0);
return true;
}
}
}
}
return false;
} // namespace WPA2
#endif // HAVE_WPA2_DECRYPTION
} // namespace Crypto
} // namespace Tins