ARM에서 Buffer Overflow를 하고, setuid와 setgid를 사용해서 관리자 권한을 획득하는 것을 실습해보자.


buffer_overflow.c 파일을 아래와 같이 만들자.

소스코드를 보면 main 함수는 vulnerable() 함수를 호출한다. 그러면 그 내부에서 strcpy()이 작동된다. 이때 buffer의 크기는 10인데, 버퍼에 복사하는 arg 포인터의 크기를 검사하는 루틴이 적용되어있지 않다. 즉, 시큐어 코딩이 적용되지 않은 취약한 상황이다. 


위에 보면, IShouldNeverBeCalled() 라는 함수가 정의되어 있다. 이 함수는 말 그대로, 절대로 호출되어서는 안되는 함수이다. 왜냐하면 setuid(0), setgid(0)를 통해 root 권한을 획득하여 /bin/bash를 실행하기 때문에 해당 시스템의 관리자 권한을 통째로 넘겨주는 것이다. 소스코드만 보면 main()에서는 결코 저 함수를 call  하지 않는다. (물론 지금은 실습을 위한 상황이다. 실제로 저렇게 위험한 기능을 프로그램에 삽입해 놓는 경우는 없을 것이다.)


ARM 장비에서 직접 gcc를 컴파일 할 수 있다면 좋겠지만 지금 본인이 실험하는 환경에서는 지원되지 않는다. 때문에 별도의 리눅스(우분투 11)에서 buffer_overflow.c를 크로스 컴파일해야 한다. apt-get install로 gcc-arm-gnueabi를 설치하면 향후 ARM용 gcc컴파일 버전으로 바이너리를 생성할 수 있다. 옵션은 armv7로 하였다.

-ggdb 옵션을 주었기 때문에 향후 gdb를 사용하여 디버깅할 수 있다.


우분투에서 작성한 파일을 ARM 디바이스로 전송하자. scp 프로토콜을 사용하면 ssh 계정 로그인 방식으로 파일이 전송된다.


이제 ARM 디바이스에 접속하면 /home/root/buffer_overflow 에 파일이 존재하며, root 소유로 되어있다. 여기에 SetUID 를 적용하면, 사용자가 해당 바이너리를 실행하는 동안 소유자(root)의 권한으로 작동하게 된다. SetGID는 그룹 권한이다. 이를 위해 chmod 4755 명령을 아래와 같이 입력해보자.

적용결과 buffer_overflow 파일에 rws 라는 권한이 붙어있다. 원래 x(execute)인 곳에 s 가 붙은 것은 setuid가 적용되어 있다는 뜻이다. setguid 하려면 2755를 주면 된다.


이제 이 파일을 /home/developer 로 복사해서 보내고, developer 계정으로 로그인하자.

buffer_overflow 파일이 있다. 이제 이 바이너리를 gdb로 실행해보자.

(gdb) 커맨드 상태로 진입하였다. 이제 우리의 목표인 IShouldNeverBeCalled() 함수의 시작위치를 확인하기 위해 disassemble 명령을 입력한다.

확인결과 0x000081d0 의 위치에 push 명령이 시작되는 것을 알 수 있다. 바로 이 주소를 추후 return address 로 덮어써야 한다.


이제는 main 함수를 disass해서 스택의 복귀주소를 확인해야 한다. 

위의 그림에서 디스어셈블된 메인함수의 어셈블리 코드를 살펴보면,  0x00008234 <+20>의 위치에서 vulnerable 함수가 호출된다. 그리고 vulnerable이 종료되고 복귀되는 주소는 바로 그 다음 주소인 0x00008238 <+24>쯤일 것이다. 


그럼 이제 gdb에서 main함수와 vulnerable함수에 브레이크 포인트를 걸고, r 명령어로 프로그램을 직접 돌려보면서 스택의 레이아웃과 복귀주소, 덮어쓸 스택의 내용 등을 점검해보자.

전달할 값으로 임의로 AAAABBBBCCCC 를 입력하였다. ASCII 값으로 바꾸면 A는 41, B는 42, C는 43이다. 브레이크 포인트에 걸리면 s(step to next line of code)를 입력하면 계속 진행된다. 두번 진행했을 때(vulnerable 함수에 있을 때) x/10x $sp 를 입력해서 메모리상의 스택포인터 내용을 직접 확인해보자.

AAAABBBBCCCC 는 41414141 42424242 43434343 일텐데, 메모리 내용에 그와같이 표기된 지점이 보인다. 0xbe86fc24에서부터 41(A)가 입력되기 시작해서 0xbe86fc28에는 42(B), 0xbe86fc2c에는 43(C)가 순차적으로 4개씩 입력되어 있다. 그리고 0xbe86fc30을 지나서 0xbe8634의 위치에는 0x00008239가 저장되어 있다. 이 값은 위에서 확인해던 main함수 상에서 vulnerable()  함수의 호출이 끝나고 복귀할 00x00008238 <+24> 근처였던 것을 기억할 때, 8239가 정확한 복귀 주소라는 것을 알 수 있다.


그러므로 이제 할 일은, 0xbe86fc30 위치까지도 덮어쓰고, 그 뒤에 있는 0xbe8634의 위치에다가 IShouldNeverBeCalled() 함수의 시작 위치를 넣어버리는 것이다.


지금까지 확인한 결과 16개의 문자가 필요하다. 아까처럼 AAAABBBBCCCCDDDD 이런식으로 넣어줘도 되지만, 이런 노가다는 향후 실전적인 상황에서 수백 수천글자를 직접 입력하게 되어 상당히 불편하다. 이럴때 보통은 printf 또는 perl 이나 python 같은 스크립트를 사용하여 아래와 같이 인라인으로 junk문자를 *16개 생성해주면 된다. 그리고 삽입하기를 원하는 16진수(hexadecimal)값을 \x81 과 같은 방식으로 붙여서 넣어주면 된다. 참고로 이를 붙여넣을 때에는 data가 little endian 방식을 따르므로 반대로 넣어주어야 한다. 입력할 때 따옴표에 주의하여야 한다. 파이썬 구문 전체를 묶는 것은 `이고, 프린트 함수 쪽은 외따옴표' 그 밖에 변수들은 쌍따옴표"으로 묶여있다. 


솔직히 나는 IShouldNeverBeCalled() 함수의 시작주소가 0x000081d0 이므로 주소 입력을 "\xd0\x81\x00\x00"이 될 것으로 예상을 했었다. 그러나 아무리 입력해도 Illegel instruction (core dumped)라는 에러가 발생했다. 그렇다고 해서 다른 값을 바꾸면 오히려 Segmentation fault (core dumped) 오류가 발생하였다. core 덤프를 계속해서 확인해보면서 두시간정도 삽질을 했다. 그러다가 우연히 \xd0을 입력하려던 손가락에 오타가 발생해서 \xd1을 타이핑하게 되었고, 갑자기 성공하는게 아닌가 -_-;;;; 뭐지??? 역시 인생은 퍼징(Fuzzing)이다. 어쨌든 결과화면은 아래와 같다. 

절대 호출되지 말았어야 할 IShouldNeverBeCalled() 함수가 호출되었고, 그 결과 setuid(0)이 걸린 채 /bin/bash/가 실행되어버렸다. root 권한이 획득된 모습이다.


솔직히 지금도 잘 모르겠다. 그냥 개인적인 추측으로는, 아까 vulnerable 함수도 종료되는 시점이 0x00008238 <+24> 일 것으로 예상했는데 실제로 복귀할 때는 0x00008239로 스택포인터에 써있던 것을 보면 1차이가 난다. 그래서 같은 원리(?)로 0x000081d0이 아닌 0x000081d1로 입력을 해야 되는게 아닌지.. 한다.


더 정확하게 잘 아시는 분 누군가 계시다면 설명해주시면 좋겠다. 왜 d0이 아니라 d1인지.. 어쨋든 아래 그림을 보면 81d1로 덮으니까 IShouldNeverBeCalled() 함수가 잘 호출되었다. 


어쨌든 BOF 에 성공해서 권한 상승이 되는 것을 실습하였다. 그러나 본 실습에서 다룬 것은 굉장히 초보적이고 제한적인 상황 하에서 실험한 것이다. 예를들어 이미 본인이 root 권한도 가지고 있다면 백도어를 만드는 경우 외에는 굳이 이렇게 일부러 취약한 프로그램안에 위험한 함수를 구현해두지는 않을 것이기 때문이다.


실제 real world에서 리눅스 상에 존재하는 바이너리의 취약점을 찾고, bof 를 이용해서 본인이 원하는 임의의 명령어를 쉘코드 형태로 실행하기 위해서는 조금 더 고급 기술이 필요하다. 또한, 리눅스 운영체제 역시 나름대로의 보안패치를 적용하였기 때문에 RTL(Return 2 Libraryc) 등의 기법을 써야한다. 관련해서는 추후에 포스팅..


참고문헌 :

Avraham, Itzhak Zuk. "Non-Executable Stack ARM Exploitation Research Paper." Revision 1 (2010): 2010-2011.

Gaurav Kumar, Aditya Gupta, A Short Guide on ARM Exploitation

ARM Exploitation

Exploiting ARM System by Tiger Security

Popping Shell on A(ndroid)RM Devices by Itzhak(Zuk) 

CPUU님의 창작활동을 응원하고 싶으세요?