Wargame/LOB

LOB golem Write-Up

0xe82de_ 2019. 8. 1. 02:44
728x90
728x90
728x90

LOB golem 풀어보자.

 

[사진 1]

이전 문제들과 다르게 함수가 추가되었다. main 함수에서는 argv 인자 갯수만 검사하고 추가된 함수인 problem_child 함수를 호출하면서 argv[1] 인자를 전달한다. problem_child 함수에서 40bytes 크기의 buffer 변수를 선언하고 strncpy 함수로 전달된 argv[1] 인자를 41bytes 복사한다. 즉, 1byte 오버플로우되는 것이다.

 

 

[사진 2]
[사진 3]

위와 같이 main 함수와 problem_child 함수의 어셈블리어 코드를 확인할 수 있다. problem_child 함수에서 strncpy 함수를 호출하기 전에 ebp-0x28 주소를 eax 레지스터에 저장한다. 즉, 이 곳이 buffer 변수의 시작점이며 여기서 ebp는 main 함수의 ebp가 아니라 problem_child 함수의 ebp이다.

 

 

[사진 4]

디버깅하기 위해 darkknight 파일을 다른 이름(karkknight)으로 복사하였다. 그리고 problem_child 함수가 종료되기 직전에 브레이크 포인트를 설정하고 임의의 값 "\x90" 40bytes와 오버플로우할 1byte "A"를 입력하였다.

그러면 위와 같이 0xbffffac4 주소부터 buffer 변수에 전달된 값들을 확인할 수 있고 0xbffffaec 주소는 problem_child 함수의 ebp 값인데 오버플로우된 1byte "A" 값으로 변조된 것을 확인할 수 있다. 그리고 buffer 변수 주소 직전 4bytes는 0xbffffac0 주소인데, 이 곳에 저장된 값은 buffer 변수의 주소이다.

 

 

[사진 5]

따라서 스택은 위와 같이 형성될 것이고, argv[1] 인자를 통해 41bytes 값을 입력하면 ebp의 첫 byte를 변조할 수 있다.

 

이 문제를 풀기 위해선 어셈블리어 leave와 ret를 잘 알아야 하는데, 각 기능은 다음과 같다.

leave

 1) move esp, ebp // ebp 레지스터의 값을 esp 레지스터에 저장한다.

 2) pop ebp // 현재 esp 레지스터가 가리키는 스택의 값을 ebp 레지스터에 저장한다.

 

ret

 1) pop eip // 현재 esp 레지스터가 가리키는 스택의 값을 eip 레지스터에 저장한다.

 2) jmp eip // eip 레지스터에 저장된 값은 다음에 실행할 명령어이므로 해당 명령어로 점프한다.

 

위 두 가지 명령어를 잘 이해하면 [사진 4]에서 확인했던 problem_child 함수의 변조된 ebp 1byte를 이용해 main 함수가 종료되고 shellcode가 실행되도록 조작할 수 있다. 결론부터 말하면 변조할 값으로 "\xbc" 값을 입력해야 하는데, 자세히 알아보자.

 

 

[사진 6]

gdb로 karkknight 파일을 실행하고 problem_child 함수가 종료되기 직전인 즉, leave 명령어가 수행되기전까지 브레이크 포인트를 설정하였다. 그리고 임의의 값 "\x90" 15bytes와 shellcode 25bytes 그리고 변조할 값 "\xbc"를 입력하였다. 사용한 shellcode는 다음과 같다.

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80

problem_child 함수의 leave 명령어가 수행되기 전까지 실행하면 위와 같이 problem_child 함수의 ebp는 0xbffffabc로 변조된다. 여기서 0xbffffabc 주소는 buffer 변수 주소보다 8bytes만큼 작은 곳이다. 그리고 buffer 변수에는 입력한 nop + shellcode가 저장되며 그 주소는 0xbffffac4이다.

 

 

[사진 7]

다음에 수행될 명령어는 problem_child 함수의 leave 명령어다.

 

 

[사진 8]

leave 명령어를 수행하기 전 ebp, esp 레지스터를 확인해보면 각각 0xbffffaec, 0xbffffac4 값이 저장되어 있다.

leave 명령어를 수행하면 위에서 설명했던 대로 먼저 move 명령어로 ebp 레지스터의 값을 esp 레지스터에 저장한다. 즉, esp 레지스터에는 0xbffffaec가 저장되는 것이다. 그리고 pop 명령어로 현재 esp 값인 0xbffffaec 값을 주소로 하는 스택의 값을 ebp 레지스터에 저장한다.  스택의 값을 pop 명령어로 뺐으므로 esp 값은 4bytes 증가한다.

따라서 위와 같이 esp 레지스터의 값은 0xbffffaf0이 되고, ebp 레지스터의 값은 스택에 저장되었던 값인 0xbffffabc가 된다.

 

 

[사진 9]

다음에 수행될 명령어는 problem_child 함수의 ret 명령어다.

 

 

[사진 10]

ret 명령어를 수행하기 전 esp 레지스터에 저장된 값이 가리키는 값을 확인해보면 0x0804849e 값이다. 이 값은 main 함수에서 problem_child 함수를 호출하고 다음에 실행될 명령어의 주소다.

ret 명령어를 수행하면 위에서 설명했던 대로 먼저 pop 명령어로 현재 esp 값인 0xbffffaf0 값을 주소로 하는 스택의 값인 0x0804849e 값을 eip 레지스터에 저장한다. 스택의 값을 pop 명령어로 뺐으므로 esp 값은 4bytes 증가한다. 그리고 jmp 명령어로 eip 레지스터에 저장된 값을 명령어로 수행하기 위해 점프한다.

따라서 위와 같이 esp 레지스터의 값은 0xbffffaf4가 되괴, eip 레지스터의 값은 0x0804849e가 된다.

 

 

[사진 11]

main 함수로 돌아와서 다음에 실행될 명령어이다. main+50의 명령어가 수행되면 esp 레지스터의 값은 4bytes 증가되어 0xbffffaf8이 된다. 그리고 leave 명령어가 수행된다.

 

 

[사진 12]

leave 명령어가 수행되기 전 ebp, esp 레지스터의 값을 확인해보면 각각 0xbffffabc, 0xbffffaf8 값이 저장되어 있다.

그리고 현재 ebp 레지스터의 값이 가리키는 값을 확인해보면 0x08048500이 저장되어 있는데, 여기선 의미 없는 값이다.

leave 명령어를 수행하면 위에서 설명했던 대로 먼저 move 명령어로 ebp 레지스터의 값을 esp 레지스터에 저장한다. 즉, esp 레지스터에는 0xbffffabc가 저장되는 것이다. 그리고 pop 명령어로 ebp 현재 esp 값인 0xbffffabc 값을 주소로 하는 스택의 값을 ebp 레지스터에 저장한다. 스택의 값을 pop 명령어로 뺐으므로 esp 값은 4bytes 증가한다.

따라서 위와 같이 esp 레지스터의 값은 0xbffffac0이 되고, ebp 레지스터의 값은 스택에 저장되었던 값인 0x08048500이 된다.

 

 

[사진 13]

다음에 수행될 명령어는 main 함수의 ret 명령어다.

 

 

[사진 14]

ret 명령어를 수행하기 전 esp 레지스터에 저장된 값이 가리키는 값을 확인해보면 0xbffffac4인데 이 값은 buffer 변수의 주소이다.

ret 명령어를 수행하면 위에서 설명했던 대로 먼저 pop 명령어로 현재 esp 값인 0xbffffac0 값을 주소로 하는 스택의 값인 0xbffffac4 값을 eip 레지스터에 저장한다. 즉 nop + shellcode가 저장되어 있는 buffer 변수의 주소가 eip 레지스터에 저장되는 것이다. 그리고 스택의 값을 pop 명령어로 뺐으므로 esp 값은 4bytes 증가하며 jmp 명령어로 eip 레지스터에 저장된 buffer 변수의 주소로 점프한다.

따라서 shellcode가 실행될 것이다.

 

 

[사진 15]

위와 같이 nop "\x90" 15bytes와 shellcode 25bytes, "\xbc" 값을 입력하면 다음 계정인 darkknight의 password를 확인할 수 있다.

728x90
728x90