qemu환경세팅이나 공유기 루트쉘 없이 디버깅하는방법

System/System 이론

2018. 12. 3. 19:43

iptime기준 qemu-user-static을 이용하여, qemu환경세팅이나 공유기 루트쉘 없이 디버깅하는방법


이 글을 쓰게 된 이유는 예전에 iot장비 펌웨어 분석팁 글을 쓰고 틈틈이 추가하고 있는데(사실 거의 추가안함), 그 내용중에 qemu-static binary로 분석하는 방법에 대해 어떤 분이 질문을 주셔서 쓰게됬다.

주변친구들이 쓰는걸 몇 번 봐서 알려져있는 방법인 줄 알았는데, 그렇게 많이 알려지지는 않은것같다.

그래서 이러한 방법이 있구나라는걸 공유해보고자 짬나는 시간에 글을 쓴다.


준비할 것

우선 분석할 펌웨어에서 파일시스템을 추출한다. (FMK나 binwalk를 이용하는방법 등등)

나는 iptime 홈페이지에 들어가서 집에서 사용하고 있는 펌웨어를 받았다.(a8004nm_kr_10_086)

또한, 해당 파일시스템에 맞는 qemu-static바이너리를 추출한 파일시스템의 적절한 곳에 복사한다.

> qemu-user-static 설치 : sudo apt-get install qemu-user-static

복사를하는 이유는 바이너리 실행 시 파일시스템의 /위치에서 chroot를 걸기 때문에, 바이너리를 실행하는 시점에서는 추출된 파일시스템을 / 로 인식하기 때문이다. 

따라서 미리 qemu-static바이너리를 옮겨놔야한다. 

> qemu-static바이너리 확인 : ls /usr/bin/qemu-* | grep static


qemu-user-static을 사용해서 펌웨어 fs 내의 바이너리 실행

일반적으로 우리가 qemu-user-static바이너리를 사용하는 경우는, 그냥 다른 아키텍쳐에서 컴파일된 바이너리를 실행하고 디버깅하고 싶을 때 귀찮아서 사용하는 경우가 대다수일 것이다.(나만 그런거일수도...)

일반적인 경우는 그냥 실행시켜도 아무에러없이 실행이 되지만, 펌웨어내의 파일시스템에 있는 바이너리를 실행 시키고 싶을 때는 조금의 트릭?이 필요하다.


우선 실행을 하기 전에, 바이너리에 대한 리버싱은 당연히 해야하고, 실행시키고 싶은, 분기하고 싶은 루틴에 대해 정확히 파악해야한다.

물론 아래에 설명할 debugging방법으로 하나하나 따라가면서 찾을수도 있다.

어쨋든, 분석이 필요한 루틴으로 가기 위한 인자 및 method방식 등은 정확히 파악해야한다.

(예를 들면, iptime에서 A라는 기능을 쓰기위해서는 timepro.cgi에 get방식으로 1의값에 A를 넣어야하고, 그 기능안에서 사용하는 인자들은 2,3,4가 있는데 여기서 bof가 터지는지 알고싶다. 여기서 2의 값에는 숫자가들어가고, 3의 값에는 mac주소, 4의 값에는 text가 들어가야한다.) 정도의 파악은 필요하다.


간단한 예를 통해서 설명하고 넘어가겠다.

내가 iptime 관리자메뉴를 돌아다니다가 guest 전용 wifi기능이 있는걸 보았고, 여기 ssid에서 bof가 터지는지 등을 알고 싶다.


날아가는 패킷을 보고 어떤 인자등이 필요한지 확인하고, ida로 해당 문자열을 기반으로 추적해서 분석을 해본다.


으으음~ 이쪽에서 분기를 하는구먼 ~ 안에 들어가서 한번보자


패킷의 body에서 보였던 인자들이 여기서 요리되는구먼~ 이제 실행을 시키면서 분석을 해볼까?


이렇게 분석을 하다가 실제로 실행을 해보고 디버깅이 필요한 시점이 있을것이다.

위 과정에서 보았던 루틴을 qemu-arm-static을 사용하여 실행시켜보고, 디버깅 해보겠다.  

(생각보다 인자들이 많아서 루틴의 초반부분까지만 실행시켜보았다.)


sudo chroot . ./qemu-arm-static -E REQUEST_METHOD="post" cgibin/timepro.cgi tmenu=iframe smenu=hiddenwlsetup wlmode=0 action=test12345

위의 명령어를 보면 대략적으로 감이 올 것이다. 

복잡한게 없다. 간단히 설명을 하자면 추출한 fs을 /라고 인식시킨 상태에서, qemu-static를 이용하여 실행을 하게 되면, 해당 바이너리가 실행되면서 필요한 라이브러리 들을 추출한 fs의 lib등에서 참조하게된다. 따라서, 단독으로 실행시킬 때 발생하는 에러들은 발생하지 않는다.

또한, 뒤의 옵션을 보면 알 수 있듯이 method방식, 인자들을 주면 친절하게 인풋을 넣을 수 있다. ㄷㄷ

다만, 위 명령어의 실행위치는 추출한 파일시스템의 / 위치여야한다.(위의 명령어 기준) 

위에도 말했다시피 내 fs이 아닌, 펌웨어의 fs을 chroot를 통해 /로 인식시켜야 하기 때문이다.


debugging 방법

위의 옵션에서 -g port옵션을 더해 ida나 gdb에 붙여서 디버깅을 할 수도 있다.

sudo chroot . ./qemu-arm-static -g 9989 -E REQUEST_METHOD="post" cgibin/timepro.cgi tmenu=iframe smenu=hiddenwlsetup wlmode=0 action=test12345


gdb에 붙일때는 gdb-multiarch 를 설치해야한다. 개인적으로는 예전부터 mips나 arm계열을 gdb로 분석할때는, pwn문제에서 자주쓰는 peda나 pwndbg보다는 gef가 더 편하고 좋은것같다. 여담이지만 gef git에 들어가면 feature설명에 peda, pwndbg랑은 달리 여긴 뭐가 잘되있다 이런 설명이 있다ㅋㅋㅋ 자신감있는듯(귀엽다)

> gef 한줄설치 : wget -q -O- https://github.com/hugsy/gef/raw/master/scripts/gef.sh | sh


ida로 attach시킬 경우.  일반적으로 gdbserver로 붙이는 것과 같다.

IDA에서 remote gdb debugger 선택하고, ip, port입력해서 디버깅하면 된다. 

잘 붙는걸 확인할 수 있다. gdb에 비해 hexray를 쓰면서 분석할 수 있는점은 큰 장점이다.


gdb-multiarch경우,  ida와 비슷하다. 다만 디버깅전에 3가지 정도 설정만 하면 된다.

set arch mips(arm .. )

set endian little(big)

target remote localhost:9989

이후에는 gdb와 동일하게 디버깅하면된다.



잘 붙어서 입력한값에 대한 디버깅도 잘 되는것을 확인할 수 있다.

유의사항?

다만, 구현되어있는 환경에 따라 모든 기능이 실제와 같게 동작하지 않을 수도 있다. 실제로 위처럼 실행했을 때, 실행이 안되는 펌웨어들도 많으며, 까다로운 설정을 해줘야 실행이 되는 것들도 있다. 이러한 것들은 사용하는 사람이 분석을 하면서 상황에 맞게 고쳐서 사용하면 될 것이다.