들어가며

그동안은 WeVO 공유기에 OpenWRT 가 포팅되었음에도 Telnet 인터페이스를 사용하거나, Command injection 취약점을 사용하는 것 외에, 웹 펌웨어 인터페이스로 손쉽게 플래싱할 수 있는 방법은 없었습니다. 이는 OpenWRT 펌웨어에 대한 접근성을 크게 떨어뜨리는 주범이었습니다.

이에 이를 해결하기 위해, WeVO 공유기 제품군의 firmware update 매커니즘을 리버싱하고, 이 위에서 손쉽게 플래싱할 수 있는 펌웨어 이미지를 생성하는 mkwevofw 툴을 제작하기까지 삽질기를 적어보고자 합니다.

Firmware update 검증 루틴 리버싱

우선 firmware update 검증 루틴을 역공학해 봅시다. 해당 루틴이 들어있는 CGI 파일인 upload.cgi 를 Ghidra 를 이용해 열어봅니다.

Ghidra 에서 펌웨어 플래싱에 사용되는 바이너리인 mtd_write 를 메모리 서치해보면 다음과 같은 문자열 결과들을 얻을 수 있습니다.

이 문자열을 참조하는 mtd_write_firmware 함수를 부르는 call tree 를 뽑아보면 main() 에서 mtd_write_firmware() 를 부름을 확인할 수 있습니다.

main() 에서는 다음과 같이 check() 를 불러서 firmware 의 무결성을 검사하고 이를 mtd_write_firmware() 를 이용해서 플래싱함을 알 수 있습니다.

check() 펑션을 살펴봅니다

해당 펑션을 살펴보면, 헤더 부분과 바이너리 전체에서 헤더를 뺀 부분의 crc32 값을 계산함을 확인할 수 있습니다.

이러한, uImage 헤더에서 헤더가 지정하는 커널 바이너리 길이를 무시하고 파일 전체에 대해 checksum 을 실행하는 구조는 iptime 의 그것과 유사합니다. 이제 가설 검증을 위해 iptime 펌웨어 체킹 스크립트를 이용해 WeVO 펌웨어의 헤더 구자가 iptime 펌웨어와 구조가 맞는지 체크해 봅니다.

결과는 다음과 같이 이미 iptime fw 구조를 가지고 있는 것을 확인할 수 있습니다.

펌웨어 빌딩

이제 OpenWRT 헤더 구조를 iptime FW 에 맞게 변형해서 플래싱해보면 다음과 같이 웹 플래싱은 통과하지만, 부팅 과정에서 오류가 발생함을 확인할 수 있습니다.

이는, U-Boot 가 펌웨어 뒤 squashfs 내용까지 ramdisk 내용으로 취급해 올려버림으로서 나타나는 문제로, 이를 해결하기 위해서는 큰 꼼수를 사용해야 합니다. 바로 CRC32 콜리전을 이용하는 것이죠.

Get MAD! ((c) Valve software, Portal 2)

WeVO 의 펌웨어 CRC32 계산 알고리즘은 헤더 구성 요소 중에 한 가지를 계산에 넣지 않았습니다. uImage 헤더 중, uImage 데이터 길이를 검증하지 않는 것이죠. 이와 더불어 CRC32 는 콜리전을 내기 쉬운 해싱 알고리즘입니다. 단지 4바이트를 덧붙이는 것 만으로도 같은 CRC32 값을 가지는 데이터를 만들어낼 수 있으니까요.

꽈광!!!

이를 이용하면, OpenWRT 펌웨어에 단지 4바이트 데이터를 덧 붙이는 것 만으로 해당 CRC 검증을 무효화하면서, U-Boot 의 정상적인 검증 루틴 또한 통과할 수 있습니다.

이를 이용해, mkwevofw 를 작성하였고, 이를 테스트해보면 다음과 같이 플래싱도 통과하고, 부팅에도 성공하는 모습을 볼 수 있습니다.

이를 이용해, 간편하게 Web UI 를 이용해 플래싱할 수 있는 WeVO 펌웨어를 만드는 데 성공하였습니다.

프로젝트 링크