[Linux] VirtFuzz 1-day
개요
해당 포스터는 VirtFuzz
에서 찾은 취약점에 대해서 RCA를 분석하기 위해 작성되었습니다. 해당 논문은 유료이기 때문에 내용은 올릴 수 없고, 현재 공개되어있는 취약점에 대해서 분석을 진행하겠습니다.
CVE-2022-42722
2개의 함수가 수정되었고, 공통적으로 rx->sdata->dev
가 존재하는지 검증하는 로직이 추가된 것을 확인할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index bd215fe3c79693..6001adc0a00e36 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1978,10 +1978,11 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
if (mmie_keyidx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS ||
mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS +
- NUM_DEFAULT_BEACON_KEYS) {
- cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
- skb->data,
- skb->len);
+ NUM_DEFAULT_BEACON_KEYS) {
+ if (rx->sdata->dev)
+ cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
+ skb->data,
+ skb->len);
return RX_DROP_MONITOR; /* unexpected BIP keyidx */
}
@@ -2131,7 +2132,8 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
/* either the frame has been decrypted or will be dropped */
status->flag |= RX_FLAG_DECRYPTED;
- if (unlikely(ieee80211_is_beacon(fc) && result == RX_DROP_UNUSABLE))
+ if (unlikely(ieee80211_is_beacon(fc) && result == RX_DROP_UNUSABLE &&
+ rx->sdata->dev))
cfg80211_rx_unprot_mlme_mgmt(rx->sdata->dev,
skb->data, skb->len);
즉, 해당 변수를 사용하는 함수를 호출하지만 값이 존재하지 않아서 Null Dereference
취약점이 발생한 것을 확인할 수 있습니다.
CVE-2022-41674
다음 내용을 보면 u8
로 선언된 변수를 size_t
로 변환한 것을 확인할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 5382fc2003db4..62f8c10412ad3 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -2279,7 +2279,7 @@ cfg80211_update_notlisted_nontrans(struct wiphy *wiphy,
size_t new_ie_len;
struct cfg80211_bss_ies *new_ies;
const struct cfg80211_bss_ies *old;
- u8 cpy_len;
+ size_t cpy_len;
lockdep_assert_held(&wiphy_to_rdev(wiphy)->bss_lock);
cfg80211_update_notlisted_nontrans
함수의 패치전은 다음과 같이 구현되어 있습니다. 여기서 문제의 변수는 cpy_len
인데 u8
자료형으로 선언되어 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
static void
cfg80211_update_notlisted_nontrans(struct wiphy *wiphy,
struct cfg80211_bss *nontrans_bss,
struct ieee80211_mgmt *mgmt, size_t len)
{
u8 *ie, *new_ie, *pos;
const struct element *nontrans_ssid;
const u8 *trans_ssid, *mbssid;
size_t ielen = len - offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
size_t new_ie_len;
struct cfg80211_bss_ies *new_ies;
const struct cfg80211_bss_ies *old;
u8 cpy_len;
...
/* copy the nontransmitted SSID */
cpy_len = nontrans_ssid->datalen + 2;
memcpy(pos, nontrans_ssid, cpy_len);
pos += cpy_len;
/* copy the IEs between SSID and MBSSID */
cpy_len = trans_ssid[1] + 2;
memcpy(pos, (trans_ssid + cpy_len), (mbssid - (trans_ssid + cpy_len)));
pos += (mbssid - (trans_ssid + cpy_len));
/* copy the IEs after MBSSID */
cpy_len = mbssid[1] + 2;
memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len)));
...
}
위의 코드에서 마지막에 있는 memcpy(pos, mbssid + cpy_len, ((ie + ielen) - (mbssid + cpy_len)))
에서 취약점이 trigger됩니다.
cpy_len
자료형과 mbssid
자료형은 u8
로 선언되어 있는데 cpy_len = mbssid[1] + 2
가 실행되면서 integer overflow가 발생하게됩니다.
CVE-2022-42719
우선, 아래의 내용부터 살펴보면 ieee802_11_elems
구조체에 멤버 변수가 추가된 것을 확인할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 4e1d4c339f2de3..a842f2e1c23096 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1709,6 +1709,14 @@ struct ieee802_11_elems {
/* whether a parse error occurred while retrieving these elements */
bool parse_error;
+
+ /*
+ * scratch buffer that can be used for various element parsing related
+ * tasks, e.g., element de-fragmentation etc.
+ */
+ size_t scratch_len;
+ u8 *scratch_pos;
+ u8 scratch[];
};
그리고 ieee802_11_parse_elems_full
함수도 패치되었습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index f61289c5fed248..99e903299143e8 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1506,24 +1506,26 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
const struct element *non_inherit = NULL;
u8 *nontransmitted_profile;
int nontransmitted_profile_len = 0;
+ size_t scratch_len = params->len;
- elems = kzalloc(sizeof(*elems), GFP_ATOMIC);
+ elems = kzalloc(sizeof(*elems) + scratch_len, GFP_ATOMIC);
if (!elems)
return NULL;
elems->ie_start = params->start;
elems->total_len = params->len;
-
- nontransmitted_profile = kmalloc(params->len, GFP_ATOMIC);
- if (nontransmitted_profile) {
- nontransmitted_profile_len =
- ieee802_11_find_bssid_profile(params->start, params->len,
- elems, params->bss,
- nontransmitted_profile);
- non_inherit =
- cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
- nontransmitted_profile,
- nontransmitted_profile_len);
- }
+ elems->scratch_len = scratch_len;
+ elems->scratch_pos = elems->scratch;
+
+ nontransmitted_profile = elems->scratch_pos;
+ nontransmitted_profile_len =
+ ieee802_11_find_bssid_profile(params->start, params->len,
+ elems, params->bss,
+ nontransmitted_profile);
+ elems->scratch_pos += nontransmitted_profile_len;
+ elems->scratch_len -= nontransmitted_profile_len;
+ non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
+ nontransmitted_profile,
+ nontransmitted_profile_len);
elems->crc = _ieee802_11_parse_elems_full(params, elems, non_inherit);
@@ -1557,8 +1559,6 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
offsetofend(struct ieee80211_bssid_index, dtim_count))
elems->dtim_count = elems->bssid_index->dtim_count;
- kfree(nontransmitted_profile);
-
return elems;
}
ieee802_11_elems
구조체에 선언된 scratch_len
길이 만큼 동적할당하는 크기가 증가했습니다. 그리고 nontransmitted_profile
변수가 가리키는 곳이 동적할당된 영역이 아닌, 새로 구조체에 선언된 elems->scratch_pos
를 가리키게 변경되었습니다.
여기서 함수 로직만 보면 취약점이 보이지 않지만 만약 multi-BSSID
로 요청을 보내게 된다면 함수가 return되기 이전에 nontransmitted_profile
변수가 해제되기 때문에 UAF 취약점이 발생하게 됩니다.
💡 multi-BSSID?
BSSID이란 와이파이 네트워크에서 특정 무선대역 또는 WLAN에 할당되는 MAC주소를 의미하고 멀티-BSSID는 말 그대로 한번에 다양한 BSSID를 사용하는 것을 의미합니다.
CVE-2022-42720
해당 취약점은 CVE-2022-42719 취약점과 동일하게 multi-BSSID
일때 발생하는 취약점입니다.
그리고 해당 취약점은 3개의 함수에서 발생한 문제때문에 CVE를 발급받았습니다. 우선, bss_ref_get
함수에서 bss 변수의 refcount
를 증가시키기전에 bss
를 초기화시키는 문제입니다. 따라서 다음과 같이 bss_from_pub
매크로를 통해서 패치되었습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index a183f2b758742d..249107212c099f 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -143,18 +143,12 @@ static inline void bss_ref_get(struct cfg80211_registered_device *rdev,
lockdep_assert_held(&rdev->bss_lock);
bss->refcount++;
- if (bss->pub.hidden_beacon_bss) {
- bss = container_of(bss->pub.hidden_beacon_bss,
- struct cfg80211_internal_bss,
- pub);
- bss->refcount++;
- }
- if (bss->pub.transmitted_bss) {
- bss = container_of(bss->pub.transmitted_bss,
- struct cfg80211_internal_bss,
- pub);
- bss->refcount++;
- }
+
+ if (bss->pub.hidden_beacon_bss)
+ bss_from_pub(bss->pub.hidden_beacon_bss)->refcount++;
+
+ if (bss->pub.transmitted_bss)
+ bss_from_pub(bss->pub.transmitted_bss)->refcount++;
}
cfg80211_bss_update
함수에서 새롭게 new
라는 변수를 kzalloc
함수를 통해서 할당합니다. 하지만, 패치전에는 pub.transmitted_bss
변수에 대해서 초기화하는 부분이 존재하지않아서 UAF
취약점이 발생하게 됩니다.
1
2
3
4
5
6
7
8
9
@@ -1741,6 +1735,8 @@ cfg80211_bss_update(struct cfg80211_registered_device *rdev,
new = kzalloc(sizeof(*new) + rdev->wiphy.bss_priv_size,
GFP_ATOMIC);
...
new->refcount = 1;
INIT_LIST_HEAD(&new->hidden_list);
INIT_LIST_HEAD(&new->pub.nontrans_list);
+ /* we'll set this later if it was non-NULL */
+ new->pub.transmitted_bss = NULL;
패치전 기준으로 __cfg80211_unlink_bss
함수를 통해서 연결을 해제하지만, 초기화하는 로직이 존재하지 않아서 취약점이 발생했습니다. 그래서 res
변수를 NULL로 초기화하는 부분이 추가되었고 해당 변수의 값이 NULL이기 때문에 return
하는 로직도 새로 추가되었습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@@ -2023,10 +2019,15 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy,
spin_lock_bh(&rdev->bss_lock);
if (cfg80211_add_nontrans_list(non_tx_data->tx_bss,
&res->pub)) {
- if (__cfg80211_unlink_bss(rdev, res))
+ if (__cfg80211_unlink_bss(rdev, res)) {
rdev->bss_generation++;
+ res = NULL;
+ }
}
spin_unlock_bh(&rdev->bss_lock);
+
+ if (!res)
+ return NULL;
}
trace_cfg80211_return_bss(&res->pub);
CVE-2022-42721
해당 취약점은 nontrans_bss->nontrans_list
의 값이 비어있을때 계속해서 list를 추가해 Infinite loop
가 발생한 취약점입니다. 따라서, 다음과 같이 nontrans_bss->nontrans_list
변수에 대한 검증을 추가했습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 249107212c099..703b05c6c43e7 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -423,6 +423,15 @@ cfg80211_add_nontrans_list(struct cfg80211_bss *trans_bss,
rcu_read_unlock();
+ /*
+ * This is a bit weird - it's not on the list, but already on another
+ * one! The only way that could happen is if there's some BSSID/SSID
+ * shared by multiple APs in their multi-BSSID profiles, potentially
+ * with hidden SSID mixed in ... ignore it.
+ */
+ if (!list_empty(&nontrans_bss->nontrans_list))
+ return -EINVAL;
+
/* add to the list */
list_add_tail(&nontrans_bss->nontrans_list, &trans_bss->nontrans_list);
return 0;