본문 바로가기

보안/DreamHack

Dreamhack Return to Library

amd64 아키텍처이고 소스코드는 다음과 같다.

 

우선 카나리와 NX가 활성화 되어있으므로, 카나리를 먼저 추출하고 버퍼 오버플로우를 통해 system('bin/bash')를 호출하면 된다.

 

gdb를 통해 main함수의 어셈블리어 코드를 보면 main함수에서 0x40바이트만큼의 스택이 할당되는 것을 볼 수 있고 이중에서 rbp-0x8에는 fs에서 0x28만큼 떨어져있는 곳의 값인 카나리가 들어가는 것을 알 수 있다. 

 

따라서 결론적으로는 0x40-0x8만큼의 버퍼가 할당이 되었고 카나리의 첫 바이트가 null인 점과 amd64 아키텍처이므로 카나리의 크기는 8바이트임을 고려하면 초기 입력값에 0x40-0x8+0x1 = 0x39만큼의 값을 입력하면 카나리의 값이 같이 출력되는 것을 생각할 수 있다.

 

위 사진처럼 입력값을 전송하면 카나리의 값이 canary_leak변수에 저장되게 된다.

 

카나리를 값을 추출함으로써 카나리를 우회할 수 있게 되었고 이제 NX만 우회하면 된다.

 

기존에는 버퍼에 쉘코드를 입력하고 return address를 버퍼의 시작주소로 덮으면 쉘이 실행이 되었지만 NX기법으로 인해 실행권한이 사라졌기에 우리는 실행권한이 있는 system함수를 이용해야 한다.

 

버퍼 오버플로우를 이용해 카나리까지 우회하고, 우리는 '/bin/bash'를 인자로 갖는 system함수를 호출할 것이다.

 

이를 위해서 '리턴 가젯'을 사용할 것이다.

 

system('/bin/bash')를 실행하기 위해서는 rdi가 '/bin/bash'인 상태에서 rsp가 system함수의 주소일 때 ret을 실행하여 rip에 system함수의 주소가 들어가고 jmp rip로 system함수를 호출해야 하는 것이다.

 

따라서 payload는 다음과 같고 이런 payload를 시각화하면 다음과 같다.

 

메인함수가 종료될 때

leave

ret

이 실행이 되는데 leave까지 실행하고 ret을 실행하기 전에 스택은 다음과 같다.

 

이때 main함수의 ret을 실행하게 되면

pop rip

jmp rip

이므로 리턴 가젯이 실행이 되고 스택이 밑 그림과 같이 바뀌게 된다.

rsp = rsp+8(pop rip때문)

이후 계산해야 하는것 

=>

pop rdi

ret

(리턴 가젯)

 

이 되게 된다.

 

이제 리턴 가젯의 pop rdi를 하게 되면 

 

또 pop을 했기 때문에 rsp = rsp+8이 되고 rdi에는 rsp였던 '/bin/bash' 가 들어가게 된다.

 

이후 리턴 가젯의 ret을 실행하면 

pop rip

jmp rip

이므로 현재 rsp의 값인 system@plt의 주소가 pop되어 rip에 저장이 되게 되고 jmp rip를 통해 rip에 들은 system@plt가 실행이 되는 것이고 rdi에는 '/bin/bash'가 들어있기 때문에 결과적으로 system('/bin/bash')가 실행이 된다.

 

그럼 payload를 더 자세하게 살펴보자

 

우선 이렇게 system@plt의 주소를 가져왔는데 ASLR이 적용되어있지 않기 때문에

디버거에서 i fu로 직접 가져와도 된다.

 

이제 나는 처음에 system_add = e.symbols['system']를 통해 엉뚱한 주소를 가져와서 계속 오류가 났다.

plt에 정의된 함수기 때문에 e.plt로 접근을 해야 정확한 주소가 나온다.

 

근데 만약에 e.plt['system']을 정확하게 했는데 무슨 missing 어쩌구 오류가 발생한다면 python3 버전을 업그레이드 해주면 된다.

 

나는 3.10.12에서 해당 오류가 발생하여 3.11.10으로 업데이트 해줬더니 오류가 사라졌다.

 

그리고 system@plt의 주소를 찾은 것과 같이 전역변수 binsh의 주소도

이렇게 찾았더니 오류가 발생해서 디버거로 확인을 해봤다.

 

페이로드를 보낸 직후에 break point를 걸어 stack을 확인해봤더니

 

이런게 보인다.

e.symbols['binsh']를 통해 얻은 0x601058이 0x400874를 참조하고 0x400874가 'binsh'를 참조하던 것이었다

 

이를 통해 e.symbols['binsh']로 binsh 변수의 주소를 얻었지만 이것은 이중 포인터이므로 system함수의 인자로 들어갈 수는 없는 것을 알 수 있었고 따로 binsh의 주소를 0x400874로 정의를 해줬다.

 

이후

리턴가젯의 주소를 추출해서 payload에 추가를 해줬고

이렇게 페이로드를 작성하게 되었다.

 

근데 여기서 뭔가 이상한 ret 변수와 해당 변수를 패킹하여 리턴 가젯을 삽입하기 이전에 payload에 들어가는 것을 볼 수 있는데 이는 system함수의 movaps 명령어때문이라고 한다.

 

system함수의 movaps 명령어는 system함수로 rip가 이동할 때 스택이 0x10 단위로 정렬되어있지 않으면 segmentation fault를 발생시킨다.

 

따라서 0x10 단위로 스택을 정렬하기 위해 아무 의미 없는 가젯을 추가를 한 것이다.

 

근데

왜 그냥 ret을 가르키는 가젯이 아무 의미없는 가젯인지는 모르겠다.

추후에 더 알아봐야 할 것 같다.

 

아무튼 movaps때문에 아무의미없는 가젯까지 페이로드에 추가하고 실행하면

 

쉘이 잘 얻어지는 것을 볼 수 있다.

'보안 > DreamHack' 카테고리의 다른 글

Dreamhack basic_rop_x64  (0) 2024.10.10
Dreamhack Return Oriented Programming  (0) 2024.10.10
Dreamhack ssp_001  (1) 2024.10.05
Dreamhack Return to Shellcode  (0) 2024.10.03
Dreamhack basic_exploitation_001  (0) 2024.10.01