원하는함수를 물어본다. 함수리스트에는
one, two, print_flag 함수가 있다. 하지만 one 함수만 작동이 된다.
main 함수다.
which function ~~ 를 출력하고,
[ebp-0x27] 에 입력을 받고,
select_func 함수의 인자로 전달해 호출한다.
[ select_func ]
[ebp-0x2a] 에 0x1F (31d) 만큼 내가입력한 문자열을 복사한다.
그리고, "one" 과 비교를해서 같으면 [ebp-0xC] 에 one 함수주소를 넣고,
[ebp-0xC] 를 eax 로 옮겨서 eax 를 호출한다.
이전 문제들과 다르게 NX, PIE 가 설정되어있고, RELRO는 FULL이다.
FULL RELRO : GOT overwrite 를 방지한다. ( GOT 주소를 조작할수없다 )
NX (Non-eXcutable) : stack, heap 에서 코드가 실행되지않게 권한을 없앤다.
PIE : 모든주소를 절대주소가 아닌 상대주소로 접근한다.
imagebase가 0x400000 처럼 고정되지않고,
실행시에 imagebase 가 결정되어 그 위치에 적재된다.
(이번문제 핵심)
우리가 입력한 문자열은
[ebp-0x2a] 부터 31 바이트니까
딱 [ebp-0xc] 까지 값을 넣을수 있다. ( 1 byte overwrite )
select_func 함수의 윗부분을 보면, [ebp-0xC] 의 초기값은
two - 1FB8 (two 함수의 주소) 인 걸 알 수 있다.
PIE 는 base 의 주소만 바꾸기때문에
[ebp-0xC] 의 초기값은 XXXXX6AD 이란걸 추측할수있고,
two : 0x000006AD, print_flag : 0x000006D8 으로
1바이트만 다르기때문에,
payload = DUMMY(30) + '\xD8' 로
[ebp-0xC] 값을 XXXXX6D8 로 1바이트 overwrite 하면
print_flag 함수가 호출된다.
1
2
3
4
5
6
7
8
9
|
from pwn import *
print_flag='\xD8'
payload='a'*30
payload+=print_flag
print(r.recvline()) #which function ~~
r.sendline(payload)
print(r.recvline()) #this function is still ~
print(r.recvline()) #flag
|