문제분석 & 풀이
코드가 너무 길기 때문에 핵심만 분석하겠다.
1) string[], check, x, count 등의 변수 선언
2) count가 100 이상이면 공격의심 문구를 출력
check == 0xdeadbeef면 shellout()을 실행하고 level19 권한의 셸을 실행한다.
3) 사용자에게 입력값을 1바이트씩 받고 변수 x에 저장
if) x의 값이 0x08이면 count--를 진행
else) x의 값이 조건 이외의 값이면 string[count]에 x 값을 저장하고 count++
level19의 권한을 얻기 위해서는 check를 0xdeadbeef로 만들어야한다.
그러나 [사진]에서 알 수 있듯이 check 변수의 위치가 입력값을 저장하는 string보다 스택 하단에 위치하기 때문에 string을 통한 버퍼오버플로우 공격이 무의미하다.
따라서 check 변수에 접근할 수 있는 다른 방법을 찾아야한다.
switch case문을 봐보자.
입력값이 0x08인 경우에는 count가 -- 된다. 그러나 default를 보면 입력값을 string에 넣을때 인덱스 지정을 count 변수를 통해 하고 있다.
count 시작 값은 0이므로 입력값이 0x08인 경우에 실행되는 조건문을 활용해서 count를 음수로 내릴 수 있다면, string보다 스택 하단에 위치하는 check 변수에 접근 할 수 있다.
정확한 스택 구조와 각 변수가 할당된 크기를 확인하기 위해서 attackme를 복사해 GDB로 디버깅했다.
check 변수의 위치를 찾기 위해서, asm 코드 중 0xdeadbeef와 비교하는 부분을 찾았다.
[ebp-104] 와 0xdeadbeef를 비교하는 것을 찾을 수 있었고 [ebp-104]가 check 변수 위치임을 알 수 있다.
c 코드를 보면 check 변수 상단에 100바이트 크기의 string 배열이 선언됐다. 따라서 스택 구조를 대략 예측해보면 다음과 같다.
우리가 입력한 값을 저장하는 string[] 바로 하단에 check가 있다.
check는 int형으로 4바이트이므로 위에서 설명했던대로 0x08 입력값을 주고 입력값을 저장하는 인덱스인 count의 값을 4번 -- 해주면, 다음번 입력값을 저장하는 주소는 check에 도달하게 된다.
0x08을 입력값으로 4번 주면 다음 저장 공간은 string[-4]이 된다.
다음으로 check에 0xdeadbeef를 저장하면 문제가 해결된다.
페이로드를 작성해보자.
(python -c 'print "\x08"*4+"\xef\xbe\xad\xde"';cat) | ./attackme
페이로드를 실행하고 password를 획득하자.