Sechack
ASLR을 우회하는 여러가지 방법 본문
ASLR로 인해서 stack, heap, libc의 base주소가 프로그램 실행할때마다 매번 변한다. 따라서 우리는 libc를 이용해서 exploit하려면 취약점을 이용해서 libc주소를 leak하고 적절한 offset연산을 거쳐서 base주소를 구해야 한다.
libc를 leak하는데는 여러가지 방법이 있다. 가장 일반적이고 떠올리기 쉬운건 fsb가 터질때 %p를 이용해서 leak하거나 bof가 터질때 rop chain으로 puts, write와 같은 출력함수들을 이용해서 함수 got와 같은 libc주소를 담고 있는 메모리 공간을 출력해주는 것이다. 아니면 스택에 libc주소가 정리 안되고 남아있을수가 있다. printf같은 함수는 NULL을 만나기 전까지 출력해주므로 스택에 libc나 stack주소가 남아있으면 딱 그 직전까지 더미데이터를 넣어서 leak할수도 있다.
여기까지가 누구나 떠올릴 수 있는 일반적인 leak방법이다.
근데 만약 출력함수가 없다면? 어떻게 libc를 leak해야할까요?
출력함수가 없어도 leak하는데는 여러가지 방법이 있다. 출력을 못하는데 대체 어떻게 leak을 할까요? 아니면 꼭 leak이 아니더라도 libc를 이용해서 exploit하는 방법이 있는걸까요?
첫번째로 출력함수 없을때 익스하는 좋은 방법중에 하나이다. 함수 got에 있는 libc주소의 1byte를 overwrite해서 syscall로 만들어버리는 것이다. 그리고 가젯이 있으면 좋고 없어도 return to csu와 같은 기법을 이용해서 인자 주고 got호출해주면 사실상 syscall이 호출이 되게 된다. pop rax가 있으면 바로 sigreturn해서 셸따면 되고 pop rax가 없어도 read함수를 사용할 경우에는 입력값의 길이로 rax를 세팅해줄 수가 있다. 만약 syscall이 함수와 조금 멀리 떨어져 있다고 해도 약간의 brute force로 해결할 수 있다.
두번째로 fflush와 같은 함수를 사용할 경우에 bss쪽에 stdout, stdin, stderr같은 fflush에서 사용하는 file stream 구조체의 주소가 쓰여지게 된다. 이걸 이용해서 bss쪽에 rop chaining을 할 fake stack을 만들어준 후 stack pivot해서 흐름을 file stream구조체 주소가 있는쪽으로 바꾸면 read와 같은 입력함수의 인자로 file stream구조체를 인자로 줄 수 있게 되면서 구조체를 변조할 수 있게 된다. 적절히 변조 한다음 fflush같은 file stream구조체 쓰는 함수 불러서 leak하고 최종적으로 셸을 따내면 된다.
세번째로는 1/4096확률의 brute force를 하는 방법이 있다. 도저히 leak할 경로가 안보이지만 main의 ret는 변조할 수 있을때 ret에는 __libc_start_main + 2**의 주소가 들어간다. 이거를 부분적으로 원가젯으로 변조해서 셸을 따내야 하는데 1.5byte ~ 2byte만큼 우리가 모르는 값이 생긴다. 따라서 이 1.5byte ~ 2byte는 brute force를 날리면서 확률을 뚫는것이다. 1/4096이라는 수치만 놓고보면 굉장히 비현실적으로 보이지만 생각보다 현실적인 수치이다. 실제로 도저히 leak할 경로가 안보이는 문제를 원가젯 brute force로 푼적이 있다. 알고보니 두번째에 소개한 방법 사용하는 거였지만...
어쨌든 1/4096확률의 brute force는 ASLR을 우회하는 가장 무식하고 단순한 방법이다. 진짜 최후의 수단으로 써야 한다. 이게 원가젯 조건 보고 brute force를 날려도 로컬에서는 되는 원가젯이 서버에서는 안될수도 있는거고 여러가지 변수가 존재한다.
ASLR을 우회해서 익스플로잇 하는 기법들을 알아보았다. 여기서 소개하지 않은 방법이 있을수도 있다. pwnable.tw에 De-ASLR문제도 gets만 있는 상태에서 full relro걸어놓고 셸따라고 한다. 혹시나 하는 마음에 gets내부 들어가봤더니 내부에서 malloc으로 힙청크 할당해주고 거기에 입력받은다음 그 힙청크에 있는 데이터를 우리가 인자로 준 스택 주소에 옮기는것 같았다. 내부에서 입력받을때 인자로 stdin구조체가 전달된다. 그리고 vtable쪽에 있는 함수들이 내부에서 사용된다. 마지막으로 이렇게 내부적으로 사용한 stdin구조체 주소는 gets를 빠져나오고 rbx인가 rcx에 남는것 같았다. 아마도 이걸 이용해서 leak하라는것 같은데 더이상은 어떻게 해야할지 모르겠다. 내공이 더 쌓이면 다시 도전해봐야겠다.
ASLR을 우회하는 방법들에 대해 알아보았다.
'Pwnable > Linux' 카테고리의 다른 글
FSB Payload 직접 짜기 (주소 쪼개기) (0) | 2021.03.05 |
---|