[하루한줄] CVE-2021-30858: Webkit의 UAF 취약점

URL

CVE-2021-30858: Use-after-free in WebKit

Target

  • iOS < 14.8, Safari < 14.1.2

Explain

브라우저 엔진인 Webkit에서 발견된 UAF 취약점의 세부 정보가 공개되었습니다.

취약점은 웹페이지에 폰트 리소스를 동적으로 로드하는 CSS Font Loading API에서 발생합니다. FontFace Lookup Table에 등록된 폰트를 제거하는 CSSFontFaceSet::removeFromFacesLookupTable 메서드의 코드는 아래와 같습니다.

void CSSFontFaceSet::removeFromFacesLookupTable(const CSSFontFace& face, const CSSValueList& familiesToSearchFor)
{
...

        auto iterator = m_facesLookupTable.find(familyName);
        ASSERT(iterator != m_facesLookupTable.end()); // Release 모드 컴파일 시 주석처리
        bool found = false;
        for (size_t i = 0; i < iterator->value.size(); ++i) {
            if (iterator->value[i].ptr() == &face) {
                found = true;
                iterator->value.remove(i);
                break;
            }
        }
        ASSERT_UNUSED(found, found);
        if (!iterator->value.size())
            m_facesLookupTable.remove(iterator);
    }
}

Lookup Table에서 폰트를 제거할 때 일치하는 폰트가 없으면 조건이 일치하지 않는 경우 예외를 발생시키는 ASSERT 문을 사용해 종료했습니다. 그러나 ASSERT는 디버깅 용도로, 디버그 모드에서만 작동하며 릴리즈 모드에서는 자동으로 주석 처리되어 작동하지 않습니다. 따라서 폰트를 Lookup Table에서 찾지 못해도 이를 제거하려고 시도합니다.

아래는 취약점의 PoC입니다.

var fontFace1 = new FontFace("font1", "", {});
var fontFaceSet = new FontFaceSet([fontFace1]);
fontFace1.family = "font2";

위와 같이 유효하지 않은 FontFace 객체를 생성한 뒤 FontFaceSet으로 등록해도 Lookup Table에 폰트가 추가되지 않지만 removeFromFacesLookupTable에서는 ASSERT가 작동하지 않아 에러 없이 추가되지 않은 폰트를 제거하려고 시도하게 되고 UAF가 트리거됩니다.

취약점은 ASSERTif문으로 대체해 iterator == m_facesLookupTable.end() 인경우 폰트 제거를 진행하지 않고 함수를 종료하는 것으로 패치되었습니다.

- ASSERT(iterator != m_facesLookupTable.end());

+ if (iterator == m_facesLookupTable.end()) {
	  return;
	}

추가 정보

(21/10/21) 해당 취약점은 CVE-2021-30858로 명명된 취약점이 아닌 다른 취약점이라는 정보가 업데이트되었습니다.