level14 이후로는mainsource의 문제를 그대로 가져왔다고 한다.
버퍼 오버플로우와 포맷스트링에 대하여 더 학습할 예정이다.
이번에도 hint와 attackme 파일이 있고, cat 명령어로 hint를 읽었다.
int 형 crap, check / 크기 20의 char형 배열 buf를 선언했다.
fgets 함수로 입력값을 45byte 만큼 받아 buf에 저장하며 표준입력을 사용한다.
if문이 실행되려면 check가 0xdeadbeef(3735928559)여야 한다.
if문 안에서는 seteuid 함수로 권한을 변경하고 쉘 스크립트를 실행한다.
buf의 크기는 20byte인데 fgets는 45byte 만큼 받아오므로 버퍼오버플로우가 발생할 수 있다.
* 버퍼 오버플로우 (Buffer overflow)
- 프로세스가 데
이터를 버퍼에 저장할 때 프로그래머가 지정한 곳 바깥에 저장하는 것
- 인접 메모리를 덮어 쓰게 되며 이때 다른 테이터가 포함되어 있을 수 있음
- 메모리 접근 오류, 잘못된 결과, 프로그램 종료, 시스템 보안 누설 발생
* 포맷 스트링 버그 (Format String Bug)
- 포맷 스트링을 인자로 하는 함수 사용 시 포맷 스트링을 지정하지 않고, 사용자 입력을 통해서 결정된다면
공격자는 이를 조작하여 메모리 내용을 참조하고 특정 영역의 값을 변경할 수 있음
- 메모리 내용을 확인(%x) 및 원하는 위치 (RET) 이동, Return Address를 악성 코드 주소로 변조해 임의 코드 실행
- printf (표준 출력) / fprintf (지정한 파일에 출력) / sprintf (지정한 버퍼에 출력)
- 서식문자열을 함수의 입력 파라미터로 직접 사용하지 않아야 함/ 정확한 포맷 스트링의 지정 필요
* fgets 함수
- 스트림에서 문자열을 받으며 오직 개행 문자에 의해서만 입력이 끝나기 때문에 띄어쓰기도 입력 가능
- str 인자: 읽어들인 문자열을 저장할 char 배열을 가리키는 포인터
- num 인자: 마지막 NULL 문자를 포함하여 읽어들일 최대 문자 수, (num - 1) 만큼을 읽음
- stream 인자: 문자열을 읽어드릴 스트림의 FILE 객체를 가리키는 포인터, 표준입력일 시 stdin
- return: 성공했다면 str 실패했다면 null 포인터를 리턴함
gdb 명령어로 attackme 파일을 분석하자.
esp에 0x38(56byte)를 할당에 주었다. buf(20) + crap(4) + check (4) = 28 이므로 dummy는 28byte이다.
<main+29>의 내용을 보면 0xdeadbeef와 ebp-16을 비교한다.
0xdeadbeef와 비교하는 값은 check이므로 이는 ebp-16에 있다.
나는 앞의 40byte를 채우고 check의 값을 0xdeadbeef로 할당하면 된다.
[메모리 구조]
RET // 4byte
SFP // 4byte
crap // 4byte
dummy // 8byte
check // 4byte
dummy // 20byte
buf // 20byte (시작주소 ebp-56
[페이로드]
buf (20byte) + dummy (20byte) + check(4byte/ 0xdeadbeef)
"A"* 40 + "\xef\xbe\xad\xde"
cat을 사용하여 명령을 넘겨받는 것은 level11에서 사용했던 이유*와 동일하다.
python -c 'print "A"*40 + "\xef\xbe\xad\xde"로 입력하면 명령을 넘겨받기 전에 종료된다.
메모리 구조를 파악하는게 아직 많이 미숙하다.
파악만 완료하면 페이로드는 바로 만들 수 있으니 천천히 생각하고 파악해야겠다.
*쉘 코드가 실행될 때 사용자의 입력을 대기해야하는데
이를 하지 않고 바로 종료되어 내가 원하는 명령을 쓰지 못하고 종료된다.
그래서 |과 cat을 사용하여 입력한 값을 쉘에게 바로 넘겨주는 방식으로 해야한다는 것을 알 수 있었다.
괄호로 묶어주는 이유는 python -c ~를 실행 후 cat|./attackme 로 넘어가는 순서이기 때문에 이를 묶어서 바로 실행시켜주기 위함이다.
_
level15의 패스워드는 "guess what"