본문 바로가기

High Level Technique/System Hacking

SigReturn Oriented Programming (SROP)

SigReturn Oriented Programming (SROP)



개념


Return Oriented Programming (ROP)의 변종으로서 system call를 이용한 기법으로 가젯이 충분하여 register를 원하는대로 조작할 수 있으면 상관 없으나 그렇지 않은 경우 사용하는 방법이다. SROP는 가젯이 많이 필요하지 않고 INT80 가젯만 있으면 가능한 기법이다.



설명



SigReturn Oritented Programming(이하 SROP)는 sigreturn을 이용하여 쉘을 따내는 기법을 말합니다.

sigreturn은 syscall table을 통하여 찾아보게 되면 아래와 같이 나타납니다.




eax값으로 119를 넣어주면 sigreturn이 실행이되는 것이죠. 그리고 sigreturn의 파라미터로 unsigend long을 요구하고 있습니다.


즉, eax로는 119를 넘겨줘야 합니다.



sigcontext.h를 참조해보면 레지스터가 들어가는 순서에 대해서 알 수 있습니다.

vim /usr/include/i386-linux-gnu/asm/sigcontext.h






자 그러면 취약한 바이너리를 하나 만들어 봅니다.

편의를 위해서 int 80과 /bin/sh를 넣어뒀습니다.




buf는 총 16개의 공간에 값을 넣을 수 있습니다. 하지만 read를 통해서 buf에 128만큼 값이 들어갑니다. 당연히  read()에서 취약점이 발생할 수 있죠.



시나리오는 다음과 같습니다.


return 주소를 int 80으로 해서 레지스터에 값을 넣어 execve를 이용하여 /bin/sh를 실행시키는 것입니다.





return 으로 int 80 주소를 준 이유는 shellcoding에 대해서 공부하셨다면 알 수 있습니다.


int 80을 이용하면 레지스터에 저장된 정보를 가지고 커널에 넘겨줍니다. syscall을 이용한 방법이죠. sigreturn은 스택에 쌓여있는 값을 가지고 레지스터에 저장시킵니다.



read()는 성공 시에 들어간 개수를 반환합니다. 따라서 sigreturn을 호출하기 위해서는 119만큼의 값을 넣어줘야 int 80을 통해서 sigreturn이 호출되죠.



그러면 먼저 임의로 119개를 맞춰서 값을 넣어보도록 하겠습니다.



python -c 'print "a"*16 + "bbbb" + "\x50\x84\x04\x08" + "C"*94' > input 이렇게 input 파일을 하나 만듭니다. 해당 개수를 세어보면 118개 인데, 마지막에는 0x10이라는 data link escape 가 들어갑니다. 뭐하는건지..?





input을 넣고 실행시키고 레지스터를 확인해 봅니다.




eax에 0x77 이 들어있는 것을 확인 할 수 있습니다. 




정확히 int 80의 위치로도 이동했습니다.


스택을 확인해 보면 아래와 같습니다.





eax가 119이고 int 80을 할테니 이제 sigreturn이 호출될 것입니다.


그럼이제 int 80을 실행시켜 보도록 하겠습니다.




sigreturn이 호출되면서 스택에 쌓여있던 값들이 레지스터로 들어가는 것을 알 수 있습니다.



바로 이것을 이용한 것입니다.







페이로드를 작성하면 아래와 같습니다.




각 레지스터를 맞게 설정해 줘야 합니다.

EAX에는 execve syscall number를 넣어주고 ebx에는 execve를 이용하여 실행할 파라미터인 /bin/sh를 넣어주는 것이죠.

그리고 마지막에는 \x00으로 채워줍니다. 119로 맞추기 위해서죠.





쉘이 실행 된 것을 알 수 있습니다.







여기서 궁금한 것이 생겼습니다.


GS, ES, DS, CS, eflags SS를 왜 설정을 해줘야 하는 걸까요?


GS : 기억장소 요구사항을 다루기 위해서 80386에서 추가된 여분의 세그먼트 레지스터

ES : 메모리 주소 지정을 다루는 스트링 연산에 사용됨. ES 레지스터를 사용할 경우, 이 레지스터를 적절한 세그먼트 주소로 초기화 해야함.

DS : 프로그램의 데이터 세그먼트의 시작 주소를 포함. 이 주소를 사용하여 데이터의 위치를 알아낸다.

CS : 프로그램의 코드 세그먼트의 시작 주소를 포함. 세그먼트 주소에 명령어 포린터 레지스터의 오프셋 값을 더하면 실행하기 위해 메모리로부터 가져와야 할 명령어의 주소가 된다.

SS : 메모리 상에 스택의 구현을 가능하게 한다. 주소와 데이터의 임시 저장 목적으로 스택을 사용한다.

eflags: 컴퓨터의 행동들의 상태를 나타내는 비트를 포함.


음... 구글에 검색해서 찾아보니 위와 같은 말이 나오는데.. 뭐가 뭔지는 잘 모르겠고...


그냥 다른 프로그램을 가지고 GDB를 이용해서 레지스터를 확인해봐면 아래와 같이 나타납니다.




하단의 레지스터들이 73, 7b, 246 이렇게 되어있는 것을 알 수 있습니다.


일단 이대로 해서 실행시켜야 정상적으로 실행된다는 것이죠.



정확히 왜 그래야 하는건지는 나중에 더 분석해 보는걸로.... 아시는 분은 댓글로 남겨주세요!