본문 바로가기

Wargame/Pwnable.kr

Pwnable.kr unlink-10pt

 unlink.c 소스를 보면 tagOBJ 라는 구조체를 선언하고 shell() 함수를 선언한다. system("/bin/sh") 함수가 있으므로 해당 코드 주소를 쉘을 따는데 이용할 수 있겠다.





 main() 함수를 보면 A,  B, C 라는 포인터 변수를 선언하고 각각을 TagOBJ 구조체의 크기만큼 힙 영역에 할당한다. 이후 구조체 안의 멤버 포인터 변수 fd와 bk 를 이용해 A <=> B <=> C 형태로 서로 가리키게 한다. 


 포인터 변수 A의 값과 A가 가리키는 힙 영역을 각각 릭해 준 뒤 A가 가리키는 구조체의 buf에 제한 없이 gets() 함수로 입력 받는다. 즉, 힙 영역에 overflow 취약점이 발생할 수 있다.





 unlink() 함수 호출 직전(A의 buf에 'AAAAAAAA'를 입력)의 힙 영역 모습이다. 표시한 부분은 각 구조체의 fd 변수인데, 구조체와 구조체 사이에 8byte 씩이 더 할당되어 있는 것은 힙 영역에 할당될 때 구조체의 정보가 함께 저장되기 때문이다. 


 각 구조체의 fd와 bk 변수의 값을 보면 A <=> B <=> C 형태인 것을 알 수 있다.






 위는 구조체 B 주소를 인자로 주어 unlink() 함수 호출 직후인데, 각각의 A구조체의 fd와 C구조체의 bk 값이 변경되었음을 알 수 있다. 즉, B가 빠지고 A <=> C 형태가 되었다. 이후 main() 함수가 종료되어 버리는데, unlink() 함수를 분석해보아야 겠다.





 unlink() 함수이다. 여기서 [ebp+0x8]은 인자로 전달된 구조체 B의 주소이다. 내용은 전달된 구조체 B의 멤버 변수 fd와 bk를 이용해 구조체 A의 fd와 구조체 C의 bk의 값을 덮어써 A와 C가 서로 가리키게끔 한다. 즉, 어떤 주소에 어떤 값을 덮어쓸 수 있게 된다.


 구조체 A가 B와 C보다 낮은 주소에 위치하므로 A의 buf를 overflow시켜 구조체 B의 fd와 bk의 값을 조작할 수 있게 되고 자세히 분석해보면 구조체 B의 fd를 AAAA, bk를 BBBB로 조작했을 경우!!


 [AAAA+4] = BBBB

 [BBBB] = AAAA        가 된다!   


 스택 상의 주소와 힙 영역의 주소를 릭해주므로 그 값을 이용해 main() 함수의 RET를 shell() 함수 주소로 덮어쓰면 되겠다.





 main() 함수의 마지막 부분을 보면 기존의 leave, ret 구조가 아니다. 



 실제 RET는 +16byte 주소에 위치하고 이 값을 EIP에 넣기 위해 EBP-0x4의 값을 ECX 레지스터에 넣고 다시 ECX-0x4를 참조해 ESP 를 조정하고 이 조정된 ESP에서 pop eip하여 EIP를 설정하는 것이다.



 따라서, dummy(16byte) + (A의 스택 주소+36) + shell()함수 주소 로 payload를 작성했으나 실패했다.

이유는 


 [AAAA+4] = BBBB   ====>   [(A의 스택 주소 +36) + 4] = shell() 주소 : 성공

 [BBBB] = AAAA       ====>   [shell() 주소] = (A의 스택 주소 +36) : 실패        


  하기 때문이다. shell() 주소를 참조하면 shell() 함수의 코드 영역이고 이 영역엔 쓰기 권한이 없기 때문에 Segmentation fault 에러가 뜨는 것이다.






 전체 작성한 exploit script 다. A의 buf에 shell() 함수 주소를 넣어주고 릭된 스택 주소에 +12를 해주어 EBP-0x4의 값을 바꿀 수 있도록 했고, 릭된 힙 주소에 +12를 해주어 결과적으로 RET가 shell() 함수 주소로 덮어써지도록 했다.



 [AAAA+4] = BBBB   ====>   [(A의 스택 주소 +12) + 4] = A의 힙 주소 +12 : 성공

 [BBBB] = AAAA       ====>   [A의 힙 주소 +12] = A의 스택 주소 +12 : 성공


 두 조건 모두 쓰기 영역이 있으므로 권한 문제가 발생하지 않고 결과적으로 RET를 직접 덮어쓰는 방법이 아니라 EBP-0x4의 값을 A의 buf주소 +4로 변경해 main() 함수의 마지막 부분을 실행한 뒤 실제 RET가 A의 buf에 쓰여진 shell() 함수 주소로 바뀌게 한 것이다.





끝.

'Wargame > Pwnable.kr' 카테고리의 다른 글

Pwnable.kr memcpy-10pt  (0) 2017.05.13
Pwnable.kr input-4pt  (0) 2017.05.09
Pwnable.kr uaf-8pt  (0) 2017.05.08
Pwnable.kr dragon-75pt  (0) 2017.05.07
Pwnable.kr simple login-50pt  (0) 2017.05.07