int $0x80

무한루프 원인 확인 및 패치 본문

컴퓨터공부/어셈블리어

무한루프 원인 확인 및 패치

cd80 cd80 2014.01.16 11:27

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

처음엔 메인함수에서 스택프레임정리와 인자정리를 했더니 무한루프가 해결돼서 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
0 Comments
댓글쓰기 폼