1) 도구: gdb, strace, Itrace, IDA
2) gdbinit: gdb를 시작할 때 자동적으로 실행할 gdb 명령어들을 저장하고 있는 파일
3) b: break point 설정
4) info: 디버깅 중인 프로세스의 정보 출력.
5) run(r): 프로세스를 실행시켜 주는 명령어
6) eip 레지스터를 통해 프로그램이 어디까지 실행되었는지 확인 가능
7) print(p): 레지스터나 변수의 값을 출력시켜줌
8) continue(c): 프로세스가 멈춰있는 상태에서 프로세스를 이어서 실행시켜 줌
9) x: 인자로 주어진 주소의 메모리를 볼 수 있음. (출력 타입 지정 가능)
10) nexti(ni): 함수의 다음 인스트럭션까지 실행해 줌
1) -p PID or –pid=PID: gdb에 -p PID 혹은 –pid PID를 인자로 전달하면
PID에 해당하는 프로세스에 gdb를 attach할 수 있음
2) 실행중인 프로세스의 PID 알아보기
- ps 프로그램 이용하기
- pidof나 pgrep 프로그램 사용하기 (pidof나 pgrep의 인자로 바이너리 이름을 전달하면 바이너리의 PID를 구할 수 있음)
3) gdb를 프로세스에 attach 시킴으로 프로세스 디버깅 가능
Info functions: 함수의 이름과 주소를 출력
Disassemble main: Main 함수의 디스어셈블리를 출력
Break: 주소에 breakpoint를 설정
Info breakpoints: Breakpoint 정보 출력
Run: 프로그램을 처음부터 실행
Display: 매 실행 시 인자로 전달된 값을 출력
Continue: 다음 브레이크포인트까지 실행
Si: Step instruction, 명령어 1개 실행
Finish: 현재 함수를 모두 실행
Info register: 레지스터 정보 출력
x/: 지정된 메모리 영역을 특정 단위로 표현
P: Print, 인자로 전달된 값을 출력
Delete [break number]: 브레이크포인터 번호에 해당하는 브레이크포인트 삭제
Quit: Gdb 종료
return address: 함수가 끝나고 돌아갈 이전 함수의 주소
스택에 저장된 리턴 주소를 다른 값으로 바꾸면 실행 흐름을 조작할 수 있음
example1에서 vuln 함수의 메모리 구조
앞에 빈공간은 buf[32]+sfp[4] = 36칸
ret 명령어: esp 레지스터가 가리키고 있는 주소에 저장된 값으로 점프하는 명령어
1) 공격자가 /bin/sh 혹은 셸 바이너리를 실행하는 기계어 코드를 실행한다면, 셸에서 제공하는 여러 명령어 실행 가능
2) execve 시스템 콜: 리눅스에서 바이너리를 실행하기 위해 사용되는 시스템 콜
1) 만들어진 셸코드를 example1 프로그램의 인자로 전달하면 셸코드가 스택 메모리에 저장됨
2) vuln 함수의 리턴 주소를 스택에 저장된 셸코드의 주소로 바꾸기
1) NOP: No OPeration의 약자, xchg eax, eax와 같이 프로그램의 실행에 영향을 주지 않는 명령어이기 때문에 프로그램이 실행 중에 NOP 명령어를 만나면 다음 명령어로 넘어가는 것과 같은 효과를 줌. 주로 명령어의 주소 alignment를 맞출 때 사용됨
2) x86 아키텍처의 NOP 명령어 바이트코드는 0x90
3) NOP Sled: 주로 셸코드의 주소를 정확히 알아내기 힘들 경우 큰 메모리를 확보하여 셸코드 주소의 오차 범위를 크게 만들 때 사용됨
1) NOP Sled를 이용하여 새로운 공격코드 만들기
1) No-eXecute bit(NX bit): 프로그램의 공격을 어렵게 하기 위해, 메모리에 쓰기 권한과 실행 권한을 동시에 부여하지 않음
2) 서로 다른 컴파일 옵션을 통해 NX bit 보호 기법을 적용한 example2_nx 바이너라와 적용하지 않은 example_x 바이너리 생성
3) example_x 바이너리를 실행하면 정상적으로 셸코드가 실행되어 셸이 실행됨. 또한 권한을 확인하면 스택과 데이터 영역 모두 rwx(읽기, 쓰기, 실행 권한)을 가지고 있음
4) example_nx 바이너리를 실행하면 데이터 영역에 실행 권한이 없기 때문에 Sementation Fault 발생. 권한을 확인하면 스택과 데이터 영역 메모리 모두 rw(읽기, 쓰기 권한)만을 가지고 있음
1) NX bit가 설정되어 있을 경우에 쓰기 권한과 실행 권한이 동시에 있는 메모리 영역은 존재하지 않음.
2) 셸코드를 스택 메모리에 저장해 실행 흐름을 스택으로 바꾸는 공격은 사용할 수 없으나, 프로그램에 스택 버퍼 오버플로우가 존재한다면 실행 흐름을 임의의 주소로 바꾸는 것은 여전히 가능함.
3) 메모리의 실행 가능한 영역에 있는 코드들을 활용해서 익스플로잇 해야 함
4) C언어에서 printf와 같은 라이브러리 함수가 사용될 때, 호출된 함수의 주소를 찾아 실행함. 그러므로 프로그램에서 호출된 함수 이외에 system과 같이 익스플로잇에 유용한 함수 코드들도 함께 로딩됨.
5) 리턴 주소를 이와 같은 방법으로 알아낸 라이브러리 함수의 주소로 바꾸면 해당하는 함수를 호출할 수 있음
1) Return To Libc(RTL): 리턴 주소를 라이브러리 내에 존재하는 함수의 주소로 바꿔 NX bit를 우회하는 공격 기법
2) libc.so.6 라이브러리에는 execve, execlp, execl, execvp, system, popen 등 프로그램을 실행할 수 있는 다양한 함수들이 존재
3) 이 중 system 함수는 인자를 하나만 받기 때문에 익스플로잇 시 많이 사용됨.
4) system 함수의 인자는 실행할 셸 명령어 문자열의 주소이기 때문에, “/bin/sh” 문자열의 주소를 system 함수의 인자로 넘겨준 후 호출하면 /bin/sh 바이너리가 실행됨
1) 스택 메모리의 권한을 검사하는 것으로도 충분함
2) ELF 바이너리 분석 도구인 readelf를 사용할수도 있음
(readelf -a (분석파일) | grep STACK)
3) 실행되고 있는 바이너리의 메모리 맵에 있는 권한 확인하기