본문 바로가기

정보보안

[문제해결] Offset 해결과정

Offset 문제

 

 

[실행 화면]

무슨 함수를 실행시킬지 물어봤는데? 아무 단어나 입력하면 아무것도 출력을 하지 않는다.

 

 

[사전조사]

 

어떤 공격기법이 통할지 조사해봤다.

Full RELRO : 저번에 함수의 진짜 주소를 다른 함수로 덮어씌우는(got 덮어쓰기) 기법이 불가능해진다.

NX enabled : 데이터가 저장되는 영역에 실행 방지가 걸려 쉘코드 삽입이 불가능하다.

PIE : 실행파일의 위치가 변하기 때문에 함수들의 절대주소는 알 수 없고, 상대적인 위치만 알 수 있다.

 

[흐름 분석]

 

원하는 함수라.. 함수의 목록을 살펴봐야겠다.

 

one 함수, two 함수, print_flag 함수가 있다.

 

print_flag 함수를 실행시키는 것이 목적인것 같다.

 

 

main 함수

우선 ebx에 offset _GLOBAL_OFFSET_TABLE이라는게 저장되는데, (got overwrite에서 got가 이것임) 이 상대 위치를 알아내보면

 

1FB8이다.

소괄호, 대괄호가 연달아 있을 경우에는 대괄호에 있는 값에서 소괄호에 있는 값을 더한다는 의미가 된다.

예를들어 저 lea eax, 구문에서는 ebx에서 (aWhichFunctionW - 1FB8h)를 빼게 되므로 1FB8 + aWhichFunctionW - 1FB8h 가 되어서 문자열 aWhichFunctionW를 정확하게 가리키게 된다.

 

뒤에서도 저 형태는 자주 등장한다.

 

저 문자열을 puts한뒤 gets를 수행하고, select_func를 수행한다.

 

 

select_func를 뜯어봐야겠다.

 

select_func

1에서 eax는 two 함수를 가리키면서 var_c에 주소값을 저장하고, 2에서 인자로 넘어온 문자열을 var_2a에 복사한다.(main에서 gets로 입력한 함수가 인자로 넘어오는것). 3에서 one 문자열과 비교하고 있으며 만약 비교 결과가 0(=같다)이 되면 4번으로 넘어가 var_c에는 one 함수가 저장된 뒤 5에서 실행되고, 만약 비교 결과가 0이 아니면 var_C에는 그대로 two 함수의 위치가 저장되기 때문에 two 함수가 실행된다.

 

one 함수

one을 입력하면 "This is function one!"이 출력될 것이고, 

 

two 함수

one 이외의 문자열은 "This is function two!" 라고 출력하도록 되어 있다.

(그런데 왜 one을 입력하면 정상적으로 출력되는데 two라고 입력하면 아무것도 안나오는지 모르겠다...)

 

[해결 전략]

비록 got overwrite는 불가능하더라도 코드 내에서는 상대적인 위치를 받아서 해당 함수로 이동하는 코드가 있기 때문에(PIE가 무의미할것), 함수의 주소를 담고있는 var_C를 변조한다면 print_flag 함수를 실행하게 해서 flag를 얻을 수 있을 것이다.

 

필요한 것은 print_flag 함수의 상대 주소, var_2a 부터 var_C까지의 거리 정도가 될것이다.

 

[필요한것들 구하기]

 

gets를 통해 입력된 문자는 arg0을 거쳐 최종적으로 var_2a로 복사되기 때문에 var_2a로부터의 거리를 구하는 것이다.

한가지 문제가 뭐냐면 strncpy 함수를 사용한다는것인데, 최대 0x1F만큼의 글자 수만큼만 옮길 수 있다.

 

즉, 아무 문자 30바이트 + 1바이트 주소만큼 옮길 수 있다는 얘기인데, print_flag, two, one의 주소는 모두 2바이트로 이루어져있다.(물론 상대주소)

 

하지만, 다행히도 two와 print_flag 함수의 경우 상위 1바이트 주소는 같기 때문에 1바이크만큼 덮어써도 two 함수 대신 print_flag 함수를 실행하도록 할 수 있다. 06AD는 실제로 \xAD\x06 이런식으로 저장되어 있는데, 입력값으로 byte 형태의 입력값을 그대로 준다면 입력한 순서대로 메모리에 쓰여지기 때문에(그동안 문제풀면서 관찰한 결과, 틀렸으면 바로 정정할 예정) 아무문자 30바이트 + '\D8' 형태로 입력을 주면 될것같다.

 

[실행으로 옮겨보기]

from pwn import *

p = remote('ctf.j0n9hyun.xyz',3007)


data = b'A'*30
data += b'\xD8'
p.sendline(data)

 

[결과]

 

성공!

 

휴.. 스택에 대한 본질적인 탐구가 필요할것 같다.