본문 바로가기

Security

TP-LINK 1day to 0day 분석글 번역 (CVE-2022-30024)

원문 정보

1day to 0day(CVE-2022-30024) on TP-Link TL-WR841N, Trần Minh Cường, (링크)

CVE-2020-8423

개요

해당 취약점을 통해 Authenticated 공격자는 WiFi 설정에 GET request를 전송하여 원격 코드 실행을 수행할 수 있다. 

취약점 분석

해당 기기는 Dispatcher( )를 통해 http request를 처리한다. REQUEST가 전송되면, httpGenListDataGet( ) 함수는 LIST_ENTRY 포인터를 리턴한다. 이 포인터는 이전에 할당된 각 URL을 처리하는 함수 포인터를 포함하는 리스트다. 이후 프로그램은 httpGenListFuncGet( ) 함수를 호출하여 리스트에서 함수 포인터를 반환받는다. 만약 핸들러 함수가 포함된 URL이 REQUEST에 발견되면, 리스트의 다음 함수 포인터를 검사한다.

프로그램은 httpRpmConfAdd( ) 함수를 사용하여 각 URL에 해당하는 핸들러 함수를 할당한다.

해당 취약점은 stringModify(char *dst, int size, char *src) 함수에서 이스케이프 문자를 처리할 때 발생한다. \, /, <, > 문자를만나면 \를 추가하거나 다음 문자가 \n이나 \r이 아닌 경우 <br>을 추가한다. 프로세스는 full-size buffer가 될 경우 중지하게 된다.

해당 함수의 문제는 dst 버퍼에 <br>을 추가할 경우, 데이터는 4bytes가 추가되는 것이지만 프로그램은 1 byte만 추가하는 것으로 진행하는 것이다.

따라서 사용자의 입력이 stringModify 함수를 호출하는 reference를 찾게 되면 해당 취약점을 트리거 할 수 있다. 트레이싱을 통해 writePageParamSet( ) 함수에서 dst[512] 버퍼가 생성되고 stringModify( )로 전달되는 것을 확인할 수 있다. (dst는 512이지만 <br>이 추가되면서 overflow 발생 가능)

또한, 0x45FA94의 /userRpm/popupSiteSurveyRpm.htm이 포함된 URL 문자열을 처리하는 콜백 함수도 제공한다.

이 함수는 GET 파라미터로 매개 변수를 가져와 취약한 함수를 호출한다.

우리는 이 취약점을 ssid 파라미터를 조종하여 트리거할 것이다.

"/%a" * 0x55 + "A" * 100 으로 payload를 작성해서 디버깅한 결과, $ra 레지스터가 변한 것이 확인 가능하고, $s0, $s1, $s2 레지스터도 overwrite 되었다.

익스플로잇

checksec을 통해 보호기법을 확인한 결과, 어떤 메커니즘도 활성화되있지 않은 것을 확인했다. 따라서 우린 스택에 쉘코드를 올려 arbitary code를 실행할 수 있다.

하지만 MIPS 아키텍쳐 환경이였기 때문에 shellcode를 실행하기 전, Instruction cache 캐시를 clear해야 했다. (Cache Incoherency Problem on MIPS Architectures 참고) 이 문제를 해결하기 위해 Sleep( )로 jump를 하게 흐름을 제어했다. 프로그램의 라이브러리를 살펴보고, 이전의 exploit에서 현재 프로그램과 일치하는 ROP 체인을 발견했다. (exploit 링크)

쉘코드 수정

해당 링크에서 쉘코드를 가져왔지만 이는 동작하지 않았다. 디버깅 결과, 쉘코드가 stringModify( )에 넘어가면서 \x3c, \x3e, \x2f, \x22, \x5c가 필터링되면서 정상적으로 동작하지 않은 것이다. (ex. \x3c -> \x5c\x3c) 이를 위해 쉘코드를 수정했다.

Fix byte 0x3c

해당 바이트는 lui instruction으로 4bytes를 스택에 저장한다. lui 명령 대신 li 명령으로 대체하여 4바이트 대신 2바이트를 스택에 저장하게 수정한다.

lui 수정 전
lui 수정 후

Fix byte 0x2f

공격자가 //bin/sh 문자열을 스택에 넣을 때, 0x3c와 0x2f가 쉘코드에 포함된다. 이를 제거하기 위해 0x3c는 위와 같이 수정했고, 0x2f는 xori 연산을 통해 0x2f bytes를 고정했다.

수정 전
수정 후

이와 같은 쉘코드 수정을 통해 성공적으로 라우터에서 코드를 실행할 수 있다.

 

CVE-2022-30024

취약점 정보

해당 취약점은 Stack-based buffer overfow 취약점으로 binary httpd로 동작하는 웹서비스 함수인 ipAddrDispose 함수에서 발생한다. 이 취약점은 GET 매개 변수값을 스택 변수에 할당하는 과정에서 발생하며, 이를 통해 사용자가 스택 메모리에 데이터를 overwrite하여 코드흐름을 제어할 수 있다.

Finding erros

ping 기능에서 에러를 찾는다. IP 주소에 문자열 aaaaaaaaa...aaa를 입력해봤지만 50글자 이상 입력할 수 없었고 ping 에러 메시지가 출력되었다. ping 요청을 전송할 때, /userRpm/PingIframeRpm.htm으로 요청한다. 

50글자 제한(maxlength)

IDA를 통해 분석한 결과, 해당 URL은 0x44A530의 핸들러 함수에서 처리되는 걸 확인할 수 있다. (callback_ping_frame( ))

httpGetEnv( ) 함수는 GET 매개변수를 통해 전달되는 ping_addr, doType, isNew 등과 같은 값을 가져오기 위해 호출된다.

ping_addr 인자는 ipAddrDispose( ) 함수로 넘어가는데 이때 취약점이 발생한다. 

지역변수(=스택변수)인 buf_ip는 52 크기이며 ping_addr로부터 값을 받는다. 문제는 ping_addr의 길이 유효성 검사를 웹 UI에서만 진행하기 때문에 쉽게 우회하여 Buffer Overflow를 유발할 수 있다. 약 200바이트의 ping_addr를 넣어 테스트해본 결과, 프로그램이 중단되며 레지스터 $ra를 덮어쓰는 것을 확인할 수 있다.

익스플로잇

이전과 마찬가지로 ASLR과 같은 보호 메커니즘은 다 꺼져있기 때문에 $ra 레지스터를 컨트롤해서 shellcode로 점프할 수 있을 것이다. buf_ip의 위치를 보면 $ra:(0xD4+8) - (0xD4-0xA0) = 168 바이트로 덮어쓸 오프셋을 계산할 수 있으며 동시에 $s0, $s1 레지스터를 더 제어할 수 있다.

결과적으로 Stack Overflow 취약점을 통해 익스플로잇에 성공했다.