RTL Chaning by RevDev


1. What is RTL Chaining?

RTL Chaining. 시스템 해킹의 세계에 발을 들여놓았다고 말할 수 있는 사람이라면 한번씩은 들어봤을 것이다. 실제로 RTL Chaining은 현재 쓰이고 있는 최신 해킹기법들의 기초이자 동시에 없어서는 안될 가장 중요한 개념이기도 하다. 이러한 RTL Chaining은 이름에서부터 알 수 있듯이 RTL(Return to Libc)기법에 기반을 두고 있으며, 어찌보면 RTL기법에서 파생되어 나온 것이라고도 할 수 있다. 물론 그렇게 볼 수도 있다. RTL이 사슬처럼 이어져서 함수가 계속해서 호출되는 것이 바로 RTL Chainig이니까. 하지만 지금은 RTL Chaining이 단순한 RTL의 일종이라고 하기에는 너무 중요도가 크기 때문에 따로 분류하는 경우가 많다. 그렇다면 이토록 중요한 RTL Chaining이란 정확히 무엇인가? 그것에 대해 다음 장에서 설명하도록 하겠다.


2.Mechanism of RTL Chaining

모두들 RTL이 무엇이다라는 것에 대하여서는 알고 있으리라 생각한다. Return To L:bc. 말 그대로 Libc내부 함수로 리턴하여 그 내부에서 새로운 함수 프롤로그를 실행하며 해당 함수를 수행하게 된다. 그렇다면 여기에 주의해서 생각해보자. 함수 프롤로그가 실행된다는 것은 함수 에필로그 또한 실행된다는 것이며, 그는 즉 RTL이후에도 RET를 통하여 프로그램의 수행을 변조할 수 있다는 것을 뜻한다. 실제로 RTL 공격 페이로드를 보며 확인해보도록 하자. 다음은 Buffer + Dummy 60Byte에 BOF취약점이 있는 프로그램에 대해 _system() Libc함수의 주소로 RET을 덮어씌운 RTL공격의 페이로드이다.

(python -c 'print "\x90"*64 + "\xd0\xcc\xe3\xf7" + "\x90"*4 + "\x5b\xe8\xf5\xf7"';cat)|./expme

우리는 이 페이로드를 통하여 페이로드 주입 이후 스택의 구조를 예측 할 수 있다. 그는 다음과 같을 것이다.

[          Args...        ][   RET    ][   SFP    ][   Variables...    ]
[  \xf7f5e85b ][\x90\x90..][\xf7e3ccd0][\x90\x90\x90\x90\x90\x90\x90...]

우리는 RTL에 대한 기반지식이 있으므로 RET의 주소로 점프 및 함수 프롤로그가 일어난 뒤의 스택 구조또한 생각할 수 있을 것이다. 여기서 주의 할 점은 RET에 넣은 주소의 함수를 호출하는 것이 아닌, 점프하여 그 ESP부터 다시 프롤로그를 수행하며 스택프레임을 만들어 나가는 것이다.

[ system Args ][   RET    ][   SFP    ][   SFP    ][   Variables...    ]
[  \xf7f5e85b ][\x90\x90..][\x90\x90..][\x90\x90\x90\x90\x90\x90\x90...]

즉, 함수 프롤로그가 끝난 후의 스택 구조는 위 그림과 같게 된다. 여기서 우리가 중요하게 살펴보아야 할 곳은 프롤로그 후의 SFP와 RET가 모드 \x90\x90\x90\x90으로 바뀌었다는 것이다. 왜 이렇게 되었을까. 다시 페이로드 주입 직후의 스택구조를 살펴보자. RET를 덮기위하여 원 함수의 SFP까지 NOP Sled로 가득 채웠다. 즉, 원 함수의 함수 에필로그가 실행 된 이후의 EBP값은 \x90\x90\x90\x90으로 바뀐다는 것이다. 여기서 우리가 덮어씌워준 RET인 Libc함수로 점프하게 되면서 ESP는 원래의 RET를 가리키게 되며, 이 상황에서 Libc함수의 프롤로그가 실행되면서 원래의 RET장소에 \x90\x90...으로 변조된 EBP가 저장되게 된다. 그렇다면 RET는 왜 \x90\x90\x90\x90을 가리키고 있는가? 이에 대한 것은 조금만 쉽게 생각하면 될 듯 하다. 변조된 EBP가 저장되어 두번째 SFP까지 만들어진 후의 ESP는 원 함수의 첫 번째 인자를 가리키고 있을 것이다. 이는 SFP+4의 위치로서 RET주소 부분에 해당된다. 즉, 원래 오버플로우를 통하여 입력해 준 RET+4부분이 새로이 호출(정확히 말하자면 JMP)된 Libc함수의 RET주소가 된다는 것이다.

그렇다면 최종적으로 뽑아낼 수 있는 결론은 무엇일까? 기존의 RTL페이로드에서 RET+4부분에 Dummy대신 다른 함수의 주소를 넣어 준다면 Libc함수가 실행된 뒤 그 함수를 수행하게 만들 수 있을것이다. 그리고 이를 계속해서 반복한다면 함수들이 꼬리에 꼬리를 물고 사슬처럼 호출(Chaining) 될 것이다.
이게 바로 RTL Chaining이다.


3. RTL Chaining Demo


간략한 문제를 직접 풀어보면서 RTL Chaining의 개념과 실사용 방법에 대하여 알아보도록 하겠다. 풀이환경은 모든 시스템해킹 기법이 통하는 FTZ Level11로 정하였다.


RTL을 하기 위해서는 가장 먼저 _system함수의 주소를 구하는 것이 최우선이다. 원래의 FTZ는 직접 디버깅이 불가능 하도록 되어있지만, 본인의 가상머신의 경우 모든 레벨의 권한을 777로 맞추어 놓아서 gdb가 붙는다. 만약 gdb가 붙지 않는다면 퍼미션을 777로 맞추던가, 아니면 복사본을 만들자. 하여튼가 gdb상에서 프로그램을 실행 시킨 뒤, _system Libc의 주소를 구해낸다. 여기서의 그 값은 0x4203f2c0이다.


그 다음으로는 BOF취약점을 공략하기 위하여 버퍼크기를 구하여야 한다. 이는 disas명령어를 통해 디스어셈블을 보면 알 수 있다. <main+3>을 보면은, sub $0x108, %esp로 버퍼의 크기가 264인 것을 알 수 있으며, 우리는 SFP까지 포함하여, 총 268Byte의 NOP Sled를 준비해야 한다는 것을 알 수 있다.


이제 _system의 주소를 통하여 /bin/sh의 주소를 구하도록 하자. 위의 코드는 memcmp를 통하여 /bin/sh문자열이 나올때까지 라이브러리 영역을 뒤지는 코드이다. 실제로 _system은 내부적으로 /bin/sh와 exec함수를 이용하여 간접적으로 명령을 실행시키기에 /bin/sh문자열이 존재할 수 밖에 없는데, 이 코드를 통하여 그 /bin/sh의 주소를 구할 수 있다.


마침내 페이로드가 완성되었다. NOP Sled로 Buffer+SFP 총 268Byte를 덮어준 뒤, 이어서 RET에 _system Libc의 주소인 0x0x4203f2c0를, Libc-RET에는 \x90\x90\x90\x90를 넣어준다. 그리고 마지막으로 _system의 인자로 들어갈 /bin/sh주소인 0x42127ea4를 추가한다.


예상대로 쉘을 획득하였다. 그러나 _system을 호출함으로서 얻은 쉘은, 종료될 때 _system의 RET이 NOP Sled인 \x90\x90...이므로 SIGSEGV를 뱉어내며 터진다. 이를 막을 수 있는 방법은 무엇일까?


간단하다. _system함수가 실행 된 뒤, 바로 이어서 프로그램을 종료시켜주는 _exit함수를 호출하자. 그러기 위해서는 _exit의 Libc주소가 필요하므로 다시 gdb를 실행시켜 구하도록 하자.

그렇게 얻은 _exit주소를 원래 페이로드에서 _system의 RET부분, 즉, 원함수의 RET바로 뒤에 붙여주면 쉘이 종료되어도(_system함수의 종료) 자연스럽게 _exit함수로 이어지며 프로그램이 종료되게 된다.
실제 상황에서의 RTL Chaining은 이것 외에도 훨씬 많은 응용을 할 수 있고, ROP(Return Oriented Programming)도 사실상 RTL의 일종이라 할 수 있을 만큼 RTL Chaining에 기반을 두고 있다. 직접 여러가지 문제를 풀면서 RTL Chaining을 응용해보자.


4. Pop-Ret Gadget

그러나 RTL Chaining의 자유로운 운용을 위해서는 Gadget이라는 것이 필요하다. 그 이유는 무엇일까. 바로 이전에 실행한 함수의 인자 때문이다. RTL 페이로드 주입 직후의 스택구조를 다시 보도록 하자.

[          Args...        ][   RET    ][   SFP    ][   Variables...    ]
[  \xf7f5e85b ][\xf7e3ceff][\xf7e3ccd0][\x90\x90\x90\x90\x90\x90\x90...]

약간 달라진 부분이라면 Libc-RET부분이 0xf7e3ceff라는 어떠한 함수의 주소로 바뀌었다는 것 정도. 그렇다면 여기서 첫번째 RTL이 실행되고 난 뒤를 예측해보자.

[ system Args ][   RET    ][   SFP    ][   SFP    ][   Variables...    ]
[  \xf7f5e85b ][\xf7e3ceff][\x90\x90..][\x90\x90\x90\x90\x90\x90\x90...]

이와 같을 것이다. 그렇게 이 Libc함수(_system)가 종료되고 에필로그가 수행되면 자연스럽게 RET위치에 있는 0xf7e3ceff함수가 이어서 실행될 것이다. 그런데 여기서 문제가 생기게 된다. 계속 진행해보자.

[     RET     ][   SFP    ][   SFP    ][   SFP    ][   Variables...    ]
[  \xf7f5e85b ][\x90\x90..][\x90\x90..][\x90\x90\x90\x90\x90\x90\x90...]

뭔가 이상하지 않는가. 첫번째 Libc함수의 Arg가 두번째 Libc함수의 RET부분이 되었다. 이렇게 되면 더 이상 Chaining이 불가능하다. 이로서 우리는 뭔가 조치가 필요하다는 것을 알 수 있다. 바로 이전에 수행되었던 함수의 인자들을 없애줄 수 있는 것을. 우리는 인자를 제거해줄 일련의 코드 조각들을 코드영역에서 추출할 수 있으며, 이를 Gadget이라고 부른다(물론 Gadget은 인자 제거만이 아닌 훨씬 큰 의미를 가진다. Gadget들이 충분하다면 우리는 코드영역, 혹은 라이브러리 영역에서 추출한 Gadget들만으로도 아예 새로운 프로그램을 만들 수 있다. 다만, RTL Chaining에서의 Gadget은 인자 제거를 맡게된다.). 그럼 이러한 상황에서 사용할 수 있는 Gadget은 뭘까. 스택에서 값을 제거할 수 있고, 동시에 원하는 스택 위치에서 RET을 해줄 수 있는 것. 바로 Pop과 Ret Opcode가 이어진 Gadget이 그 역할을 해줄 수 있을 것이다. 이러한 Pop-Ret Gadget은 수행한 Libc함수의 인자 개수에 따라 Ret앞에 붙는 Pop의 갯수가 달라지며 부르는 명칭또한 달라진다.
가령 예를들어 ‘Strcpy를 수행했다. 인자 두개를 썼다.’ 라고 한다면 인자 두 개를 없애주기위해 Ret앞에 Pop이 두 개 붙어있는 가젯을 써야 할 것이다. Pop이 두 개 붙은 Gadget은 Pop과 Ret의 앞글자를 따서 ppr Gadget이라 칭한다. 그렇다면 인자가 네 개인 Send와 같은 함수는? 당연히 ppppr Gadget이다.

그럼 다시 위 스택구조로 돌아가자. 이제는 알 수 있다. RET을 집어넣기 전에 _system의 인자 하나를 없애주기 위해 pr Gadget을 사용해야 한다. 그렇다면 어떤식으로 페이로드를 짜야할까? 만약 pr Gadget의 주소가 0x42037efb라고 한다면 Exploit직후의 스택구조는 다음과 같아야 할 것이다.

[                 Args...                ][   RET    ][   SFP    ][  Variables... ]
[  \xf7e3ceff ][  \xf7f5e85b ][\x42037efb][\xf7e3ccd0][\x90\x90\x90\x90\x90\x90...]

그럼 방금 번의 스택구조와 어떤부분에서 다른지 알아보자. 첫번째 Libc함수가 실행되었다고 하자. 그때의 스택구조는 다음과 같을 것이다.

[ Meaningless ][ system Args ][   RET    ][   SFP    ][   SFP    ][  Variables... ]
[  \xf7e3ceff ][  \xf7f5e85b ][\x42037efb][\x90\x90..][\x90\x90\x90\x90\x90\x90...]

여기까지는 딱히 다를게 없어보인다. 그러나 Libc-RET이 pr Gadget으로 바뀌면서 이 다음부터의 코드 수행은 크게 달라질 것이다. 그럼 pr Gadget을 실행시켰다고 하자.

[     RET     ][ POP(Erased) ][   SFP    ][   SFP    ][   SFP    ][  Variables... ]
[  \xf7e3ceff ][  \xf7f5e85b ][\x90\x90..][\x90\x90..][\x90\x90\x90\x90\x90\x90...]

보다시피 중간에 끼여있던 _system의 인자가 pr Gadget에 붙어있던 Pop에 의해 지워지고 RET이 정상적으로 다음에 수행해야 할 0xf7e3ceff함수를 가리키게 하였다. 이제 우리가 처음 의도한 대로 0xf7e3ceff함수를 호출할 수 있게 되었다. 야호.


5. So, this is RTL Chaining

위에서 보여주었듯이 계속해서 적절히 Gadget과 Libc함수를 조합하면 끝없이 Libc함수를 연쇄 호출하는 것 또한 가능하다. 사슬처럼 Libc함수가 이어지고 이어지는 것, 이것이 바로 진정한 RTL Chaining이다. 만약 당신이 진짜 해커라면 이렇게 자유롭게 함수를 호출 할 수 있는 RTL Chaining기법을 이용한 온갖 응용 공격법이 떠오를 것이다. 당신은 조금 더 자유롭게 해킹을 할 수 있게 되었고, 프로그램을 어떤식으로 공격할지 생각할 수 있는 선택지가 더 많아졌으며, 어떤식으로 공격 순서를 배열할 것인지 미리 설정할 수도 있게 되었다. 축하한다. 이제 바로 리눅스를 켜고 직접 원하는 함수들을 조립하고 조립해서 공격을 해보도록 하자. 거기서 좀 더 나아가서 ROP라는 가장 흥미로운 분야에 발을 디뎌보자. 그래, 이것이 바로 RTL Chaining이다.

Posted by RevDev
,

[Malloc]

-Allocate Space in the Heap.

Reference:

void* malloc (size_t size);


1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
 
int main()
{
    const char *strHello = "Hello Heap Allocate!";
    char *ptrHello;
    ptrHello=(char *)malloc(strlen(strHello)+1);
    strcpy(ptrHello,strHello);
      printf("%s",ptrHello);
      free(ptrHello);        
    return 0;
}
cs




[Alloca]

-Allocate Space in the Stack.

Reference:

void* alloca (size_t size);


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <alloca.h>
 
int main()
{
    const char *strHello = "Hello Stack Allocate!";
    char *ptrHello;
    ptrHello=alloca(strlen(strHello)+1);
    strcpy(ptrHello,strHello);
    printf("%s",ptrHello);    
    return 0;
}
 
cs



Posted by RevDev
,


WhiteHat 2015 Junior Pre-Qual

-정신나간놈들 WriteUp-






WhiteHat 2015 Junior Pre-Qual WriteUp.pdf




Posted by RevDev
,