취약점 제보글: https://hackerone.com/reports/480883

notepad-plus-plus/PowerEditor/src/Notepad_plus.cpp의 xml파싱 기능중 오버플로우가 존재한다는 내용입니다.

Notepad++는 오픈소스  프로젝트로 공식 github에서 소스코드를 다운로드 받을 수 있습니다.

https://github.com/notepad-plus-plus/notepad-plus-plus/releases/tag/v7.6.2 취약점이 존재하는 7.6.2버전의 소스코드를 다운로드 받고, 해당 부분을 분석해보겠습니다.

 

949번째 줄 부터 시작되는 getHtmlXmlEncoding함수입니다.

int Notepad_plus::getHtmlXmlEncoding(const TCHAR *fileName) const
{
	// Get Language type
	TCHAR *ext = PathFindExtension(fileName);
	if (*ext == '.') //extension found
	{
		ext += 1;
	}
	else
	{
		return -1;
	}
	NppParameters *pNppParamInst = NppParameters::getInstance();
	LangType langT = pNppParamInst->getLangFromExt(ext);

	if ((langT != L_XML) && (langT != L_HTML))
		return -1;

가장 처음 부분에 fileName으로 파일의 확장자를 받아서 ext에 저장하는 것을 볼 수 있습니다. 

이후 getLangFromExt함수로 해당 파일의 언어 타입을 받아와 XML과 HTML이 아닌 경우 return을 하게됩니다.

	// Get the beginning of file data
	FILE *f = generic_fopen(fileName, TEXT("rb"));
	if (!f)
		return -1;
	const int blockSize = 1024; // To ensure that length is long enough to capture the encoding in html
	char data[blockSize];
	size_t lenFile = fread(data, 1, blockSize, f);
	fclose(f);

	// Put data in _invisibleEditView
	_invisibleEditView.execute(SCI_CLEARALL);
	_invisibleEditView.execute(SCI_APPENDTEXT, lenFile, reinterpret_cast<LPARAM>(data));

	const char *encodingAliasRegExpr = "[a-zA-Z0-9_-]+";

이후 파일을 열고 xml의 encoding부분을 받아오기 위해 1024바이트 만큼 파일을 읽어옵니다.

if (langT == L_XML)
	{
		// find encoding by RegExpr

		const char *xmlHeaderRegExpr = "<?xml[ \\t]+version[ \\t]*=[ \\t]*\"[^\"]+\"[ \\t]+encoding[ \\t]*=[ \\t]*\"[^\"]+\"[ \\t]*.*?>";

        size_t startPos = 0;
		size_t endPos = lenFile-1;
		_invisibleEditView.execute(SCI_SETSEARCHFLAGS, SCFIND_REGEXP|SCFIND_POSIX);

		_invisibleEditView.execute(SCI_SETTARGETRANGE, startPos, endPos);

		auto posFound = _invisibleEditView.execute(SCI_SEARCHINTARGET, strlen(xmlHeaderRegExpr), reinterpret_cast<LPARAM>(xmlHeaderRegExpr));
		if (posFound != -1 && posFound != -2)
		{
            const char *encodingBlockRegExpr = "encoding[ \\t]*=[ \\t]*\"[^\".]+\"";
			_invisibleEditView.execute(SCI_SEARCHINTARGET, strlen(encodingBlockRegExpr), reinterpret_cast<LPARAM>(encodingBlockRegExpr));

            const char *encodingRegExpr = "\".+\"";
			_invisibleEditView.execute(SCI_SEARCHINTARGET, strlen(encodingRegExpr), reinterpret_cast<LPARAM>(encodingRegExpr));

			_invisibleEditView.execute(SCI_SEARCHINTARGET, strlen(encodingAliasRegExpr), reinterpret_cast<LPARAM>(encodingAliasRegExpr));

            startPos = int(_invisibleEditView.execute(SCI_GETTARGETSTART));
			endPos = _invisibleEditView.execute(SCI_GETTARGETEND);

            char encodingStr[128];
            _invisibleEditView.getText(encodingStr, startPos, endPos);

			EncodingMapper *em = EncodingMapper::getInstance();
            int enc = em->getEncodingFromString(encodingStr);
            return (enc==CP_ACP?-1:enc);

startPos와 endPos를 초기화 해둔 뒤, 아래 부분에서 SCI_GETTARGETSTART와 SCI_GETTARGETEND로 encoding부분의 시작과 끝을 구합니다.

그 뒤 getText함수로 128바이트의 encodingStr에 startPos와 endPos사이의 데이터를 받아옵니다.

이때 encoding을 받아오는 배열이 128바이트로 정해져 있기 때문에 encoding이 128바이트가 넘어가면 버퍼오버플로우가 발생하게됩니다.

 

복사했습니다!