현재 bof9의 id로 접속한 상태이고, bof10의 권한을 얻고싶은 상황이다.
bof9로 home directory를 보면 bof9
, bof9.c
파일과 bof10의 비밀번호가 있는 bof10.pw
파일이 있다.
bof10의 비밀번호는 bof10
파일에 저장되어 있다. 그러나, bof10.pw
를 읽기 위해서는 bof10의 권한이 필요한데, 마침 bof9
파일이 bof10권한으로 실행되므로 이 프로그램을 이용해보도록 하자.
bof9.c 코드
코드를 분석해보면 vuln()
함수로 들어가 system
의 주소와 "/bin/sh"
가 저장되어 있는 binsh
을 출력하고
gets
로 입력을 받아 buf
에 저장하고 buf
의 내용을 출력한다.
이 때 vuln
함수로 들어간다는 점을 이용해 stack에 저장되어 있는 return할 주소를 /bin/sh
가 적혀있는 주소로 바꿔주면 bof10권한으로 쉘을 실행시킬 수 있을 것 같다. gets
함수의 overflow로 return 주소를 바꿔보자.
gdb를 이용해 buf와 return address가 저장된 곳의 거리를 구해보도록 하자
-
return address 저장 주소
stack에 저장된 return 주소
$rsp 값
vuln
함수에 들어오면 가장 먼저 return address를 stack에 저장한다. 이 값은 $rsp에 저장되어 있으므로 $rsp의 값을 읽었다. -
buf 주소
buf의 주소는 아래에서gets
에서buf
의 주소를 사용하므로, 이 때 $rdi를 보면 알 수 있다. -
buf - innocent 사이의 거리
- return address가 저장된 주소 0x7fffffffe498
- buf 주소 0x7fffffffe488
둘의 차이를 계산해보면 16byte 차이가 난다.
우리가 이제 해야할 일은 return address를 조작하는 것이다.
그런데, 이전과는 다르게 이 파일은 다음과 같이 stack에서는 'execution'권한이 없다.
이럴 경우에는 실행권한이 있는 곳으로 우회해야 하는데,
libc 라이브러리에는 실행권한이 있으니 libc의 system
함수에 "/bin/sh"
이 적혀있는 주소를 argument로 주면 쉘을 실행할 수 있을 것이다.
"/bin/sh"
의 주소는 위처럼 gdb에서 찾을 수 있다.
-
system
에 argument 넣기
system
함수는$rdi
의 값을 argument로 받는다. 그렇다면$rdi
에"/bin/sh"
을 넣으면 되는데, 이 과정은 libc에서pop rdi ; ret
명령이 기계어로 저장되어 있는 부분을 실행하고 pop할 곳을"/bin/sh"
의 주소로 넣어주면$rdi
에 들어갈 것이다.
libc는 위에서 찾은 것처럼/tmp/2.23/lib/libc.so.6
에 있고, 가젯을 찾는ROPgadget.py
프로그램을 이용해pop rdi ; ret
의 가젯의 주소를 찾았다. 위의 것들 중 정확하게 일치하는0x21102
일 것이다. 이는 libc.so.6에서의 offset 주소이므로, libc.so.6의 주소에 해당 주소를 더해주면 실행했을 때의 주소가 나올 것이다.
따라서 0x7ffff7a0d000 + 0x21102 = 0x7ffff7a2e102 이다. -
payload 구조
최종적으로 만든 payload의 구조는 다음과 같다.쓰레기값 return 주소 "/bin/sh" 주소 system 함수 주소 의미 없음 pop rdi ; ret "/bin/sh" system() 값 x 0x7ffff7a2e102 0x7ffff7b99d57 0x7ffff7a52390 크기 16byte 8byte 8byte 8byte 위처럼 해주면
vuln
함수가 끝나 return 할 때pop rdi ; ret
를 실행하는 주소로 return되어pop rdi
와ret
을 실행하여 뒤에 over flow로 들어간"/bin/sh"
가$rdi
로 들어가고,ret
에 의해system
이 call 되어 최종적으로system("/bin/sh")
이 실행된다.