System/Windows

Notepad++_v7.6.3 Command Injection

pyozzi 2019. 6. 13. 23:37

얼마전에 Windows 기본 프로그램 메모장에서 RCE가 발견됐다는 기사를 본 적이 있었다.

보자마자 드는 생각이 '가능해?'라는 생각이 들었는데, Notepad++ 에서도 취약점이 발견됐다고 한다.


이것도 마찬가지로 처음에 드는 생각이 '가능한가?' 였다.

나는 Notepad++를 써본 적이 없어서 기본 메모장처럼 글만 쓰는 아주 베이직한 프로그램일 줄 알았다.


근데 공격벡터가 아예 없는건 아니였다.

Search Engine을 설정해서 구글이나 야후등의 검색 서비스 기능도 있었고,

각종 플러그인으로 추가적인 기능들을 프로그램에서 사용할 수 있었다.


이번에 발견된 취약점 재현 절차는 다음과 같으며, v7.6.3이하의 버전에서만 유효하다.

1. [설정] - [환경 설정] - [Search Engine]에서 사용자의 Custom Engine을 정의할 수 있다.


2. Custom Engine 설정칸에 커맨드를 입력한다. (cmd /k calc)


3. [편집] - [선택 시] - [인터넷에서 검색] 을 선택하는 순간 입력했던 커맨드가 실행된다.

출처: https://hackerone.com/reports/495382


[ Exploit! ]


[ Vuln Analysis ]

Notepad++은 오픈소스로 공개된 프로그램이기 때문에 깃허브에서 소스코드를 받아볼 수 있다.


[Notepad++ Github]

https://github.com/notepad-plus-plus/notepad-plus-plus


PowerEditor/src/NppCommands.cpp ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
case IDM_EDIT_SEARCHONINTERNET:
        {
            if (_pEditView->execute(SCI_GETSELECTIONS) != 1// Multi-Selection || Column mode || no selection
                return;
 
            const NppGUI & nppGui = (NppParameters::getInstance())->getNppGUI();
            generic_string url;
            if (nppGui._searchEngineChoice == nppGui.se_custom)
            {
                if (nppGui._searchEngineCustom.empty())
                {
                    url = TEXT("https://www.google.com/search?q=$(CURRENT_WORD)");
                }
                else
                {
                    url = nppGui._searchEngineCustom.c_str();
                }
            }
            else if (nppGui._searchEngineChoice == nppGui.se_duckDuckGo)
            {
                url = TEXT("https://duckduckgo.com/?q=$(CURRENT_WORD)");
            }
            else if (nppGui._searchEngineChoice == nppGui.se_google)
            {
                url = TEXT("https://www.google.com/search?q=$(CURRENT_WORD)");
            }
            else if (nppGui._searchEngineChoice == nppGui.se_bing)
            {
                url = TEXT("https://www.bing.com/search?q=$(CURRENT_WORD)");
            }
            else if (nppGui._searchEngineChoice == nppGui.se_yahoo)
            {
                url = TEXT("https://search.yahoo.com/search?q=$(CURRENT_WORD)");
            }
 
            Command cmd(url.c_str());
            cmd.run(_pPublicInterface->getHSelf());    
        }
        break;
cs

취약점은 위 코드에서 발생된다.


[편집] - [선택 시] - [인터넷에서 검색] 옵션을 클릭하면 실행되는 코드인데


 if (nppGui._searchEngineChoice == nppGui.se_custom)
            {
                if (nppGui._searchEngineCustom.empty())
                {
                    url = TEXT("https://www.google.com/search?q=$(CURRENT_WORD)");
                }
                else
                {
                    url = nppGui._searchEngineCustom.c_str();
                }
            }

검색엔진이 Custom Engine으로 설정되어 있으면 위 분기문에 진입한다.

만약, 선택만 되어있고 설정칸이 비어 있으면 google로 설정하고

설정칸에 값이 있으면 url변수에 사용자가 입력한 값을 저장한다.



          
  Command cmd(url.c_str());
            cmd.run(_pPublicInterface->getHSelf());  

그 후, 별도의 검사없이 cmd.run( )으로 명령을 실행한다.


정상적인 경우라면 

https://www.google.com/search?q=$(CURRENT_WORD)

이런 값이 cmd( )의 인자로 넘어가서 브라우저가 실행 됐겠지만,

사용자가 URL이 아닌 임의의 명령을 넣어놨을 경우, 해당 명령이 실행되는 취약점이 발생하게 되는 것이다.


[ How to Patch? ]

해당 취약점은 어떻게 패치되었는지 확인해보자

취약점은 v7.6.4에서 패치되었으며, 아래는 패치된 코드 부분이다.



[ Notepad++ v7.6.4 ]

1
2
3
4
5
6
7
8
9
10
11
12
13
if (nppGui._searchEngineChoice == nppGui.se_custom)
            {
                url = nppGui._searchEngineCustom;
                remove_if(url.begin(), url.end(), isspace);
 
                auto httpPos = url.find(TEXT("http://"));
                auto httpsPos = url.find(TEXT("https://"));
 
                if (url.empty() || (httpPos != 0 && httpsPos != 0)) 
                {
                    url = TEXT("https://www.google.com/search?q=$(CURRENT_WORD)");
                }
// if string is not a url (for launching only browser)
            }

cs

취약점이 패치된 버전인 v7.6.4에는 위 코드가 추가되었다.


[ 첫 번째 패치포인트 ]

remove_if(url.begin(), url.end(), isspace);

- 명령의 인자를 넘기기위해 사용되는 공백값을 검사하고, 만약에 발견되면 없앤다.


[두 번째 패치포인트 ]

                auto httpPos = url.find(TEXT("http://"));
                auto httpsPos = url.find(TEXT("https://"));
 
                if (url.empty() || (httpPos != 0 && httpsPos != 0)) 
                {
                    url = TEXT("https://www.google.com/search?q=$(CURRENT_WORD)");
                }

- 값의 "http://" 와 "https://" 포함되어 있는지 확인한다.

그 후, 입력값이 없거나 HTTP포맷이 발견되지 않으면 Google로 강제 설정한다.


[ 여담 ]

처음 Notepad++에서 취약점이 발견됐다고 했을 때는 "오??" 했다가,

취약점을 재현했을 때는 "음??" 했다가,

취약점을 분석했을 때는 "오!!" 했다.


"음??"이라는 반응의 이유를 말해보자면, 사실 소스코드 분석없이 Notepad++를 사용하다가

저 취약점을 발견했을 때 나는 취약점으로 느끼지 않았을지도 모른다는 생각이 들었다.


"음? 커맨드가 실행되네.." 하고 넘겼을지도 모르겠다.


이 취약점이 트리거되려면, [편집] - [선택 시] - [인터넷에서 검색]을 누르는건 둘째 치고

우선 설정창에 들어가서 검색엔진에 Command를 넣어줘야 한다.


공격 시나리오를 생각해봤을 때, 카페에서 Notepad++를 사용하는 사람이 자리 비운틈에 설정을 바꾼다!

뭐 이런 시나리오가 제일 현실적으로 떠오른다.


한마디로 말하자면, 시나리오가 너무 제한적이다.


그 때문인지 버그바운티도 100만원 선에서 책정된 것 같다.


물론, 소스코드를 분석해보니 확실히 취약점이 맞다라는 생각이 든다.

이렇게 Vulnerability의 범위를 좀 더 명확하게 알아가는 것 같다.